[erlang-questions] Encode and decode binary data on port driver: undefined symbol ei_decode_version

37 views
Skip to first unread message

Brisa Jiménez

unread,
Nov 22, 2012, 2:09:18 PM11/22/12
to erlang-q...@erlang.org
Hi everyone!
I want to send and receive binary data through Erlang port, and use ei to encode and decode data, but when I try to test code below the next error occurs:

Erlang R14B03 (erts-5.8.4) [source] [rq:1] [async-threads:0] [kernel-poll:false]

Eshell V5.8.4  (abort with ^G)
1> erl_drv:start().
** exception exit: {error,"undefined symbol: ei_decode_version"}
     in function  erl_drv:start/0

Can anybody give me an example of how do it what I'm want?
I appreciate any advice.
Thanks.

===

-module(erl_drv).
-export([start/0, stop/0, init/1]).
-export([foz/1, baz/1]).
-define(C_LIBRARY, "library").

start() ->
    case erl_ddll:load_driver("priv", ?C_LIBRARY) of
ok -> ok;
{error, already_loaded} -> ok;
{error, Reason}-> exit({error, erl_ddll:format_error(Reason)})
    end,
    spawn(?MODULE, init, [?C_LIBRARY]).

init(?C_LIBRARY) ->
    register(erl_drv, self()),
    Port = open_port({spawn, ?C_LIBRARY}, [binary]),
    loop(Port).

stop() ->
    erl_drv ! stop.

foz(Arg)->
    call_port({foz, Arg}).

baz(Arg)->
    call_port({baz, Arg}).

call_port(Msg) ->
    erl_drv ! {call, self(), Msg},
    receive
        {erl_drv, Res} ->
   Res
    end.

loop(Port) ->
    receive
{call, Caller, {Command, Data}}->
   case Command of
foz-> port_control(Port, 3, encode(Data));
baz-> port_control(Port, 4, encode(Data))
   end,
   
   receive
       {Port, {data, Data}} ->
   Caller ! {erl_drv, decode(Data)}
       end,
   loop(Port);
    stop ->
   Port ! {self(), close},
   receive 
{Port, closed} ->
   exit(normal)
   end;
    {'EXIT', Port, Reason} ->
   io:format("~p ~n", [Reason]),
   exit(port_terminated)
    end.

encode(Msg)->
    term_to_binary(Msg).

decode(RetVal)->
    binary_to_term(RetVal).

===

/* port_driver.c*/

#include <stdio.h>
#include "erl_driver.h"
#include "ei.h"
#include "erl_interface.h"

double foz(double);
double baz(double);

typedef size_t ErlDrvSizeT;
typedef ssize_t ErlDrvSSizeT;

typedef struct {
    ErlDrvPort port;
} ptr_port;

static ErlDrvData start(ErlDrvPort port, char *buff)
{
    ptr_port* ptr_port1 = (ptr_port*)driver_alloc(sizeof(ptr_port));
    set_port_control_flags(port , PORT_CONTROL_FLAG_BINARY);
    ptr_port1->port = port;
    return (ErlDrvData)ptr_port1;
}

static void stop(ErlDrvData handle)
{
    driver_free((char*)handle);
}

static ErlDrvSSizeT control(ErlDrvData handle, unsigned int command,
                            char *buf, ErlDrvSizeT len,
char **rbuf, ErlDrvSizeT rlen)
{
ptr_port *ptr_port1 = (ptr_port*)handle;
    double arg, res;
    int version, index = 0;

    if (ei_decode_version(buf, &index, &version))
        return((ErlDrvSSizeT) ERL_DRV_ERROR_GENERAL);
    if (ei_decode_double(buf, &index, &arg))
        return((ErlDrvSSizeT) ERL_DRV_ERROR_BADARG);
    switch (command) {
        case 3:
            res = foz(arg);
            break;
        case 4:
            res = baz(arg);
            break;
        default:
            return((ErlDrvSSizeT) ERL_DRV_ERROR_BADARG);
    }
    index = 0;
    if (ei_encode_version(*rbuf, &index)
            || ei_encode_double(*rbuf, &index, res))
        return((ErlDrvSSizeT) ERL_DRV_ERROR_ERRNO);
    else
        return((ErlDrvSSizeT) index);
}

ErlDrvEntry driver_entry = {
    NULL, //* F_PTR init, N/A 
    start, //* L_PTR start, called when port is opened 
    stop, //* F_PTR stop, called when port is closed 
NULL, //* F_PTR output, called when erlang has sent 
    NULL, //* F_PTR ready_input, called when input descriptor ready 
    NULL, //* F_PTR ready_output, called when output descriptor ready 
    "library", //* char *driver_name, the argument to open_port 
    NULL, //* F_PTR finish, called when unloaded 
    control, //* F_PTR control, port_command callback 
    NULL, //* F_PTR timeout, reserved 
    NULL //* F_PTR outputv, reserved 
};
 
DRIVER_INIT(library) /* must match name in driver_entry */
{
return &driver_entry;
}

===

/*library.c*/

double foz(double x) {
return x/1.0;
}

double baz(double y) {
return y/2.0;
}

===

# Makefile

ERL_ROOT=/usr/local/lib/erlang

all: compile_c compile

compile_c:
@mkdir -p priv
gcc -o priv/library.so -I$(ERL_ROOT)/usr/include/ -I$(ERL_ROOT)/lib/erl_interface-3.7.4/include/ -fpic -shared c_src/*.c 

compile:
@mkdir -p ebin
erlc -o ebin/ src/erl_drv.erl

clean:
@rm -rf ebin priv

test:
erl -pa ebin/
.PHONY: c_src


Jachym Holecek

unread,
Nov 22, 2012, 2:00:15 PM11/22/12
to Brisa Jim?nez, erlang-q...@erlang.org
# Brisa Jim?nez 2012-11-22:
> I want to send and receive binary data through Erlang port, and use ei to
> encode and decode data, but when I try to test code below the next error
> occurs:
>
> Erlang R14B03 (erts-5.8.4) [source] [rq:1] [async-threads:0]
> [kernel-poll:false]
>
> Eshell V5.8.4 (abort with ^G)
> 1> erl_drv:start().
> ** exception exit: {error,"undefined symbol: ei_decode_version"}
> in function erl_drv:start/0
>
> [...]
>
> # Makefile
>
> ERL_ROOT=/usr/local/lib/erlang
>
> all: compile_c compile
>
> compile_c:
> @mkdir -p priv
> gcc -o priv/library.so -I$(ERL_ROOT)/usr/include/
> -I$(ERL_ROOT)/lib/erl_interface-3.7.4/include/ -fpic -shared c_src/*.c

You're not linking against ei so runtime linker complains about unresolved
symbols when you attempt to load your driver.

BTW Your Makefile doesn't look completely right either, I don't see why not
use filenames as source/target names allowing make to work as intended, but
even if there were a reason you should declare your targets as .PHONY (how
exactly is that done depends on make dialect at hand).

BR,
-- Jachym
_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://erlang.org/mailman/listinfo/erlang-questions
Reply all
Reply to author
Forward
0 new messages