Port Driver, outputv, binaries.

Matthew Sackman matthew@REDACTED
Wed Dec 9 19:58:51 CET 2009


Hi,

So I'm writing a port driver for a linked in binary. I've implemented
outputv, and that seems to be working fine.

In order to see what I'm being sent, I have the following in C:

void dump_ev(ErlIOVec *ev) {
  printf("total size: %d\r\nvec len: %d\r\n", ev->size, ev->vsize);
  int idx;
  for (idx = 0; idx < ev->vsize; ++idx) {
    printf("iov[%d] = ", idx);
    SysIOVec iov = ev->iov[idx];
    printf("[base = %p, len = %zd]\r\n", iov.iov_base, iov.iov_len);
    printf("binv[%d] = ", idx);
    if (NULL == ev->binv[idx]) {
      printf("NULL\r\n");
    } else {
      ErlDrvBinary* bin = ev->binv[idx];
      printf("[orig_bytes = %p; orig_size = %zd]\r\n", bin->orig_bytes, bin->orig_size);
    }
  }
  printf("done\r\n");
}

which is called at the start of outputv:

static void test_outputv(ErlDrvData drv_data, ErlIOVec *ev)
{
  dump_ev(ev);
  ...
}

Now, in the shell, I do the following:

1> ok = erl_ddll:load_driver("ebin", "libtest"), Port = open_port({spawn_driver, libtest}, [binary]).
2> port_command(Port, [<<2>>,<<0,0,0,0,0,0,0,0>>,<<0>>,<<0>>,<<0>>]).

And I get out the following:

total size: 12
vec len: 6
iov[0] = [base = (nil), len = 0]
binv[0] = NULL
iov[1] = [base = 0x17a89b0, len = 1]
binv[1] = [orig_bytes = 0x17a89b0; orig_size = 1]
iov[2] = [base = 0x17a89e8, len = 8]
binv[2] = [orig_bytes = 0x17a89e8; orig_size = 8]
iov[3] = [base = 0x17a8a20, len = 1]
binv[3] = [orig_bytes = 0x17a8a20; orig_size = 1]
iov[4] = [base = 0x17a8a58, len = 1]
binv[4] = [orig_bytes = 0x17a8a58; orig_size = 1]
iov[5] = [base = 0x17a8a90, len = 1]
binv[5] = [orig_bytes = 0x17a8a90; orig_size = 1]
done

Which makes a lot of sense - each arg corresponds to an entry in the iov
and binv. That's all fine.

Now, in some erlang code (a gen_server) I have this:

init([]) ->
    erl_ddll:start(),
    ok = erl_ddll:load_driver("ebin", ?LIBNAME),
    Port = open_port({spawn_driver, ?LIBNAME}, [binary, stream]),
    {ok, Port}.

handle_call({tune, BNum, APow, FPow, Opts}, _From, Port) ->
    Data = [<<2/native>>,
            <<BNum:64/signed-integer-native>>,
            <<APow:8/signed-integer-native>>,
            <<FPow:8/signed-integer-native>>,
            <<Opts:8/native>>],
    port_command(Port, Data),
    ...

Then in the shell I do this:

1> {ok, Pid} = test_drv:start_link(), gen_server:call(Pid, {tune, 0, 0, 0, 0}).

and I get out:

total size: 12
vec len: 2
iov[0] = [base = (nil), len = 0]
binv[0] = NULL
iov[1] = [base = 0x1faded0, len = 12]
binv[1] = [orig_bytes = 0x1faded0; orig_size = 12]
done

Why on earth has it gone and pushed all those binaries into one big
binary? What on earth is the actual behaviour meant to be? Where is it
defined? How can I reliably pull args out of the iov and binv? Certainly
the behaviour is different to that in
http://www.erlang.org/pipermail/erlang-questions/2006-March/019818.html

In short, WTF?!

Matthew


More information about the erlang-questions mailing list