[erlang-bugs] A dets big ?

Tomas Abrahamsson tomas.abrahamsson@REDACTED
Sun Jul 28 16:48:49 CEST 2013


>>>>> "Manuel" == Manuel Durán Aguete <manuel@REDACTED> writes:
>>
>> After upgrading a project from R14B03 to R16B01 I've found that
>> dets files are growing constanly after delete operations.
>>
>> I've uploaded a test case to github: http://kcy.me/oz1s
>
> After some research the problem seem to be related to a
> dets:traverse/2 after the dets file is opened.
>
> I've updated the code in github with new tests, now it calls dets
> directly.

Hi,

   I, too, have seen a(nother) strange issue with dets,
it might possibly be the same root cause.  I saw
bad_object_header errors.

A short summary of what I found is that when Erlang is
compiled with gcc 4.8.1, at least on a 32-bit linux, code in
efile_drv.c start acting unexpectedly, causing eg file:pread
with 2 positions to fail with {error,einval}.

I managed to boil my initial dets issues down to this test
program:

    -module(detstest).
    -export([start/0]).

    start() ->
        F = "detstest",
        file:delete(F),
        {error, enoent} = file:read_file_info(F),
        {ok, Db} = dets:open_file(d, [{file,F}, {keypos,2}]),
        %% need 2 entries for the error to show
        ok = dets:insert(Db, {a, 1}),
        ok = dets:insert(Db, {b, 2}),
        io:format("~p~n", [dets:close(Db)]).

If Erlang is compiled with gcc-4.8.1, it prints
{error,{bad_object_header,"detstest"}},
while I would have expected it to print just ok.

I traced the dets issue down to a call to file:pread, and
managed to boil it down a bit further to this test program:

    -module(pread).
    -export([start/0]).

    start() ->
        {ok,Fd} = file:open("pread-test", [write,raw,binary,read]),
        file:write(Fd, <<"abcdef\n">>),
        io:format("~p~n", [file:pread(Fd, [{0,1}])]),
        io:format("~p~n", [file:pread(Fd, [{1,1}])]),
        io:format("~p~n", [file:pread(Fd, [{0,1}, {1,1}])]),
        file:close(Fd).

When Erlang is compiled with gcc-4.8.1, it prints:

    {ok,[<<"a">>]}
    {ok,[<<"b">>]}
    {error,einval}

while with gcc-4.7.3, it prints what I would expect:

    {ok,[<<"a">>]}
    {ok,[<<"b">>]}
    {ok,[<<"a">>,<<"b">>]}

With the change in the diff below, pread, and also dets,
starts to behave as expected again, but this is not a
complete bugfix. I don't fully understand what's going on,
and similar code is at many places in efile_drv.c. That's
why I indented the diff---it is only an illustration---so
nobody can take it for a complete solution.

Even a seemingly unrelated change, eg inserting
fprintf(stdout,"sizeof(q)=%d",sizeof(q)); just before the if
statement, instead of applying the diff, will also make the
file:pread behave as expected. Perhaps the code is
runs into some kind of undefined behaviour in C?

Some info:
  - Erlang:  compiled OTP_R16B01 from git
  - System:  debian (unstable), core i5, 32-bits
  - Kernel:  3.10-1-686-pae
  - gcc:     gcc (Debian 4.8.1-8) 4.8.1
             with this version, pread fails with einval
  - gcc-4.7: gcc-4.7 (Debian 4.7.3-4) 4.7.3
             with this version, pread and dets behaves as expected.

I found I had the problem beacause debian's erlang
(r16.b.1-dfsg-4) was compiled with gcc-4.8, and I couldn't
recreate the issue when compiling from git, until I upgraded
gcc.

  diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
  index 595b048..65ce49a 100644
  --- a/erts/emulator/drivers/common/efile_drv.c
  +++ b/erts/emulator/drivers/common/efile_drv.c
  @@ -3746,9 +3746,14 @@ 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)
  -               || !EV_GET_UINT32(ev, &sizeH, &p, &q)
  -               || !EV_GET_UINT32(ev, &sizeL, &p, &q)) {
  +           int gotOffsets;
  +           int gotSizeH;
  +           int gotSizeL;
  +
  +           gotOffsets = EV_GET_UINT64(ev, &d->c.preadv.offsets[i-1], &p, &q);
  +           gotSizeH   = !gotOffsets || EV_GET_UINT32(ev, &sizeH, &p, &q);
  +           gotSizeL   = !gotSizeH   || EV_GET_UINT32(ev, &sizeL, &p, &q);
  +           if (!gotOffsets || !gotSizeH || !gotSizeL) {
                  reply_posix_error(desc, EINVAL);
                  break;
              }


BRs
Tomas



More information about the erlang-bugs mailing list