aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/beam/erl_io_queue.c7
-rw-r--r--erts/emulator/drivers/common/efile_drv.c61
-rw-r--r--erts/emulator/pcre/README.pcre_update.md2
-rw-r--r--erts/emulator/test/efile_SUITE.erl49
-rw-r--r--erts/emulator/test/emulator_smoke.spec1
-rw-r--r--erts/emulator/test/iovec_SUITE.erl43
6 files changed, 133 insertions, 30 deletions
diff --git a/erts/emulator/beam/erl_io_queue.c b/erts/emulator/beam/erl_io_queue.c
index a01b676d39..190ba6bbb9 100644
--- a/erts/emulator/beam/erl_io_queue.c
+++ b/erts/emulator/beam/erl_io_queue.c
@@ -973,9 +973,10 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
UWord binary_size;
Uint byte_offset, bit_offset, bit_size;
+ byte *binary_data;
+
Eterm *parent_header;
Eterm parent_binary;
- byte *binary_data;
ASSERT(state->bytereds_available > state->bytereds_spent);
@@ -1001,14 +1002,14 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
erts_emasculate_writable_binary(pb);
}
- binary_data = pb->bytes;
+ binary_data = &((byte*)pb->bytes)[byte_offset];
} else {
ErlHeapBin *hb = (ErlHeapBin*)parent_header;
ASSERT(thing_subtag(*parent_header) == HEAP_BINARY_SUBTAG);
ASSERT(is_bin_small);
- binary_data = &((unsigned char*)&hb->data)[byte_offset];
+ binary_data = &((byte*)&hb->data)[byte_offset];
}
if (!is_bin_small && (state->acc_size == 0 || !is_acc_small)) {
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
index a1b15a2199..4e1d2f0d7f 100644
--- a/erts/emulator/drivers/common/efile_drv.c
+++ b/erts/emulator/drivers/common/efile_drv.c
@@ -1261,6 +1261,50 @@ static void free_read_line(void *data)
EF_FREE(d);
}
+void read_file_zero_size(struct t_data* d);
+#define ZERO_FILE_CHUNK (64 * 1024)
+
+/* [ERL-327] Some special files like /proc/... have reported size 0 */
+void read_file_zero_size(struct t_data* d) {
+ size_t total_read_size = 0;
+ size_t allocated_size = ZERO_FILE_CHUNK; /* allocd in invoke_read_file */
+ for (;;) {
+ size_t read_result;
+
+ /* Read until we hit EOF (read less than FILE_SEGMENT_READ) */
+ d->result_ok = efile_read(&d->errInfo,
+ EFILE_MODE_READ,
+ (int) d->fd,
+ (d->c.read_file.binp->orig_bytes +
+ total_read_size),
+ ZERO_FILE_CHUNK,
+ &read_result);
+ if (!d->result_ok) {
+ break;
+ }
+
+ total_read_size += read_result;
+ d->c.read_file.offset += read_result;
+ if (read_result < ZERO_FILE_CHUNK) {
+ break;
+ }
+
+ /* Grow before the next read call */
+ allocated_size = total_read_size + ZERO_FILE_CHUNK;
+ d->c.read_file.binp = driver_realloc_binary(d->c.read_file.binp,
+ allocated_size);
+ }
+
+ /* Finalize the memory usage. Hopefully it was read fully on the first
+ * go, so the binary allocation overhead becomes:
+ * alloc ZERO_FILE_CHUNK (64kb) -> realloc real_size */
+ if (allocated_size != total_read_size) {
+ d->c.read_file.binp = driver_realloc_binary(d->c.read_file.binp,
+ total_read_size);
+ }
+ d->again = 0;
+}
+
static void invoke_read_file(void *data)
{
struct t_data *d = (struct t_data *) data;
@@ -1279,9 +1323,15 @@ static void invoke_read_file(void *data)
}
d->fd = fd;
d->c.read_file.size = (int) size;
- if (size < 0 || size != d->c.read_file.size ||
- ! (d->c.read_file.binp =
- driver_alloc_binary(d->c.read_file.size))) {
+
+ /* For zero sized files allocate a reasonable chunk to attempt reading
+ * anyway. Note: This will eat ZERO_FILE_CHUNK bytes for any 0 file
+ * and free them immediately after (if the file was empty). */
+ ERTS_ASSERT(size >= 0);
+ d->c.read_file.binp = driver_alloc_binary(size != 0 ? (size_t)size
+ : ZERO_FILE_CHUNK);
+
+ if (size < 0 || size != d->c.read_file.size || !d->c.read_file.binp) {
d->result_ok = 0;
d->errInfo.posix_errno = ENOMEM;
goto close;
@@ -1290,6 +1340,11 @@ static void invoke_read_file(void *data)
}
/* Invariant: d->c.read_file.size >= d->c.read_file.offset */
+ if (d->c.read_file.size == 0) {
+ read_file_zero_size(d);
+ goto close;
+ }
+
read_size = (size_t) (d->c.read_file.size - d->c.read_file.offset);
if (! read_size) goto close;
chop = d->again && read_size >= FILE_SEGMENT_READ*2;
diff --git a/erts/emulator/pcre/README.pcre_update.md b/erts/emulator/pcre/README.pcre_update.md
index 8caf575d31..599e3d0d12 100644
--- a/erts/emulator/pcre/README.pcre_update.md
+++ b/erts/emulator/pcre/README.pcre_update.md
@@ -2,7 +2,7 @@
## The basic changes to the PCRE library
-To work with the Erlang VM, PCRE has been changed in two important ways:
+To work with the Erlang VM, PCRE has been changed in three important ways:
1. The main execution machine in pcre\_exec has been modified so that
matching can be interrupted and restarted. This functionality utilizes
diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl
index f0e1bcf04b..08d5597d78 100644
--- a/erts/emulator/test/efile_SUITE.erl
+++ b/erts/emulator/test/efile_SUITE.erl
@@ -19,16 +19,20 @@
-module(efile_SUITE).
-export([all/0, suite/0]).
--export([iter_max_files/1, async_dist/1]).
+-export([async_dist/1,
+ iter_max_files/1,
+ proc_zero_sized_files/1
+ ]).
-export([do_iter_max_files/2, do_async_dist/1]).
-include_lib("common_test/include/ct.hrl").
+-include_lib("stdlib/include/assert.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [iter_max_files, async_dist].
+ [iter_max_files, async_dist, proc_zero_sized_files].
do_async_dist(Dir) ->
X = 100,
@@ -162,3 +166,44 @@ open_files(Name) ->
% io:format("Error reason: ~p", [_Reason]),
[]
end.
+
+%% @doc If /proc filesystem exists (no way to know if it is real proc or just
+%% a /proc directory), let's read some zero sized files 500 times each, while
+%% ensuring that response isn't empty << >>
+proc_zero_sized_files(Config) when is_list(Config) ->
+ {Type, Flavor} = os:type(),
+ %% Some files which exist on Linux but might be missing on other systems
+ Inputs = ["/proc/cpuinfo",
+ "/proc/meminfo",
+ "/proc/partitions",
+ "/proc/swaps",
+ "/proc/version",
+ "/proc/uptime",
+ %% curproc is present on freebsd
+ "/proc/curproc/cmdline"],
+ case filelib:is_dir("/proc") of
+ false -> {skip, "/proc not found"}; % skip the test if no /proc
+ _ when Type =:= unix andalso Flavor =:= sunos ->
+ %% SunOS has a /proc, but no zero sized special files
+ {skip, "sunos does not have any zero sized special files"};
+ true ->
+ %% Take away files which do not exist in proc
+ Inputs1 = lists:filter(fun filelib:is_file/1, Inputs),
+
+ %% Fail if none of mentioned files exist in /proc, did we just get
+ %% a normal /proc directory without any special files?
+ ?assertNotEqual([], Inputs1),
+
+ %% For 6 inputs and 500 attempts each this do run anywhere
+ %% between 500 and 3000 function calls.
+ lists:foreach(
+ fun(Filename) -> do_proc_zero_sized(Filename, 500) end,
+ Inputs1)
+ end.
+
+%% @doc Test one file N times to also trigger possible leaking fds and memory
+do_proc_zero_sized(_Filename, 0) -> ok;
+do_proc_zero_sized(Filename, N) ->
+ Data = file:read_file(Filename),
+ ?assertNotEqual(<<>>, Data),
+ do_proc_zero_sized(Filename, N-1).
diff --git a/erts/emulator/test/emulator_smoke.spec b/erts/emulator/test/emulator_smoke.spec
index b2d0de8835..fc98ba6823 100644
--- a/erts/emulator/test/emulator_smoke.spec
+++ b/erts/emulator/test/emulator_smoke.spec
@@ -7,3 +7,4 @@
[consistency],"Not reliable in October and March"}.
{cases,'Dir',crypto_SUITE,[t_md5]}.
{cases,'Dir',float_SUITE,[fpe,cmp_integer]}.
+{cases,'Dir',erts_debug_SUITE,[df]}.
diff --git a/erts/emulator/test/iovec_SUITE.erl b/erts/emulator/test/iovec_SUITE.erl
index 28df36d293..49dc64b0d2 100644
--- a/erts/emulator/test/iovec_SUITE.erl
+++ b/erts/emulator/test/iovec_SUITE.erl
@@ -24,7 +24,8 @@
-export([integer_lists/1, binary_lists/1, empty_lists/1, empty_binary_lists/1,
mixed_lists/1, improper_lists/1, illegal_lists/1, cons_bomb/1,
- iolist_to_iovec_idempotence/1, iolist_to_iovec_correctness/1]).
+ sub_binary_lists/1, iolist_to_iovec_idempotence/1,
+ iolist_to_iovec_correctness/1]).
-include_lib("common_test/include/ct.hrl").
@@ -34,8 +35,8 @@ suite() ->
all() ->
[integer_lists, binary_lists, empty_lists, empty_binary_lists, mixed_lists,
- illegal_lists, improper_lists, cons_bomb, iolist_to_iovec_idempotence,
- iolist_to_iovec_correctness].
+ sub_binary_lists, illegal_lists, improper_lists, cons_bomb,
+ iolist_to_iovec_idempotence, iolist_to_iovec_correctness].
init_per_suite(Config) ->
Config.
@@ -46,15 +47,16 @@ end_per_suite(Config) ->
integer_lists(Config) when is_list(Config) ->
Variations = gen_variations([I || I <- lists:seq(1, 255)]),
+ equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
- equivalence_test(fun erlang:iolist_to_iovec/1, Variations),
-
- ok.
+sub_binary_lists(Config) when is_list(Config) ->
+ Parent = <<0:256/unit:8, "gazurka">>,
+ <<0:196/unit:8, Child/binary>> = Parent,
+ equivalence_test(fun erlang:iolist_to_iovec/1, gen_variations(Child)).
binary_lists(Config) when is_list(Config) ->
Variations = gen_variations([<<I:8>> || I <- lists:seq(1, 255)]),
- equivalence_test(fun erlang:iolist_to_iovec/1, Variations),
- ok.
+ equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
empty_lists(Config) when is_list(Config) ->
Variations = gen_variations([[] || _ <- lists:seq(1, 256)]),
@@ -70,8 +72,7 @@ empty_binary_lists(Config) when is_list(Config) ->
mixed_lists(Config) when is_list(Config) ->
Variations = gen_variations([<<>>, lists:seq(1, 40), <<12, 45, 78>>]),
- equivalence_test(fun erlang:iolist_to_iovec/1, Variations),
- ok.
+ equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
illegal_lists(Config) when is_list(Config) ->
BitStrs = gen_variations(["gurka", <<1:1>>, "gaffel"]),
@@ -82,18 +83,15 @@ illegal_lists(Config) when is_list(Config) ->
Variations =
BitStrs ++ BadInts ++ Atoms ++ BadTails,
- illegality_test(fun erlang:iolist_to_iovec/1, Variations),
-
- ok.
+ illegality_test(fun erlang:iolist_to_iovec/1, Variations).
improper_lists(Config) when is_list(Config) ->
Variations = [
[[[[1 | <<2>>] | <<3>>] | <<4>>] | <<5>>],
- [[<<"test">>, 3] | <<"improper tail">>],
- [1, 2, 3 | <<"improper tail">>]
+ [[<<1>>, 2] | <<3, 4, 5>>],
+ [1, 2, 3 | <<4, 5>>]
],
- equivalence_test(fun erlang:iolist_to_iovec/1, Variations),
- ok.
+ equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
cons_bomb(Config) when is_list(Config) ->
IntBase = gen_variations([I || I <- lists:seq(1, 255)]),
@@ -108,8 +106,7 @@ cons_bomb(Config) when is_list(Config) ->
end,
Variations = gen_variations([IntBase, BinBase, MixBase], Rounds),
- equivalence_test(fun erlang:iolist_to_iovec/1, Variations),
- ok.
+ equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
iolist_to_iovec_idempotence(Config) when is_list(Config) ->
IntVariations = gen_variations([I || I <- lists:seq(1, 255)]),
@@ -134,11 +131,15 @@ iolist_to_iovec_correctness(Config) when is_list(Config) ->
ok.
illegality_test(Fun, Variations) ->
- [{'EXIT',{badarg, _}} = (catch Fun(Variation)) || Variation <- Variations].
+ [{'EXIT',{badarg, _}} = (catch Fun(Variation)) || Variation <- Variations],
+ ok.
equivalence_test(Fun, [Head | _] = Variations) ->
+ %% Check that each variation is equal to the others, and that the sum of
+ %% them is equal to the input.
Comparand = Fun(Head),
- [is_iolist_equal(Comparand, Fun(Variation)) || Variation <- Variations],
+ [true = is_iolist_equal(Comparand, Fun(V)) || V <- Variations],
+ true = is_iolist_equal(Variations, Fun(Variations)),
ok.
is_iolist_equal(A, B) ->