Showing posts with label libtre. Show all posts
Showing posts with label libtre. Show all posts

Thursday, December 20, 2007

Server DNS problems, and no longer available downloads :/

My own box has some troubles with its DNS entries, this means that you can no longer reach the LibTre (treregex-0.7)...

I've set up an googlepage to store it in the mean time: easyerl.googlepages.com.

Why did I take some much time to fix it ? That's because I've left Paris to work in the South of France, exactly "sur la côté d'Azur".

I'll publish some other simple things in the near future... So you just need to subscribe to my Feed :).

Download libTre.

PS: Now if you read the mailing list you'll be able to retrieve the package...

Sunday, November 18, 2007

Treregex-0.7 download now

You can download the treregex-0.7 from here.

To test it you need to install the libtre library, normally you can do this by using apt-get:

-apt-cache search libtre
libtre-dev - development package for the libtre4 regexp matching library
libtre4 - regexp matching library with approximate matching


Install the libtre4 and libtre-dev, and then try to ./configure the treregex. You may need to edit Makefile adjusting path for your needs...

And as usual, feedback is welcome :p

Thursday, November 15, 2007

An gen_server for mass regexp computing... (LibTre)

This is the first test session of my 'tregex_srv' that provides some nice regexp features:

266> l(tregex_srv).
{module,tregex_srv}
267> tregex_srv:start_link().
{ok,<0.4045.0>}
268> tregex_srv:store( [<<"[0-9+] pid">>, <<"[a-z]+.tmp">>]).
ok
269> tregex_srv:grep(<<"test 9405904.tmp acuu.tmpmulaor 10+ pid">>).
[[[{34,39,<<"+ pid">>}],[{17,25,<<"acuu.tmp">>}]]]
270> tregex_srv:store( [{ <<"test">>, fun(X) -> io:format("found: ~p~n", [X]) end}, <<"[0-9][0-9]">>]).
ok
271> tregex_srv:grep(<<"test 9405904.tmp acuu.tmpmulaor 10+ pid">>).
found: [{0,4,<<"test">>}]
[[[{34,39,<<"+ pid">>}],[{17,25,<<"acuu.tmp">>}]],
[[{0,4,<<"test">>}],[{5,7,<<"94">>}]]]
272> tregex_srv:store( [{ <<"SRC=[^ ]+">>, fun(X) ->
[{_,_,M}] = X, io:format("Source: ~p~n", [M])
end}]).
ok
273> tregex_srv:grep(<<"test 9405904.tmp acuu.tmpmulaor 10+ pid">>).
found: [{0,4,<<"test">>}]
[[[{34,39,<<"+ pid">>}],[{17,25,<<"acuu.tmp">>}]],
[[{0,4,<<"test">>}],[{5,7,<<"94">>}]],
[]]
274> tregex_srv:grep(<<"tst SRC=192.135.15.1 pid">>).
Source: <<"SRC=192.135.15.1">>
[[[{19,24,<<"1 pid">>}]],
[[{8,10,<<"19">>}]],
[[{4,20,<<"SRC=192.135.15.1">>}]]]
275> tregex_srv:store( [{ <<"SRC=([^ ]+)">>, fun(X) ->
[{_,_,_}, {_,_,M}] = X, io:format("Source IP: ~p~n", [M])
end}]).
ok
276> tregex_srv:grep(<<"tst SRC=192.135.15.1 pid">>).
Source IP: <<"192.135.15.1">>
Source: <<"SRC=192.135.15.1">>
[[[{19,24,<<"1 pid">>}]],
[[{8,10,<<"19">>}]],
[[{4,20,<<"SRC=192.135.15.1">>}]],
[[{4,20,<<"SRC=192.135.15.1">>},{8,20,<<"192.135.15.1">>}]]]

As you can see, you can associate Funs with regexp Matches. This means that you can bind action to regexp...
First we store (in fact add regexp to the existing regexp list) new tuples {RE, Fun}:

275> tregex_srv:store( [{ <<"SRC=([^ ]+)">>, fun(X) ->
[{_,_,_}, {_,_,M}] = X, io:format("Source IP: ~p~n", [M])
end}]).
ok

Now the exec does call already registered funs, but call the new one since our regexp matches and you can see that the IP number is only printed, the "submatches" feature works as expected:

276> tregex_srv:grep(<<"tst SRC=192.135.15.1 pid">>).
Source IP: <<"192.135.15.1">>
Source: <<"SRC=192.135.15.1">>
...

The gen_server state is the following:

-record(state, {
requests,
reindex,
re = [],
pids = []
}).

Its init function is:

init(_Args) ->
process_flag(trap_exit, true),
{ok, #state{
re = ets:new(?MODULE, [set,private]),
requests = 0,
reindex = 1 }}.

Internally the module calls 'treregex:compile' to compile regexp and store the resulting #port into a list that is stored in the 'ets' table. Every call to 'tregex_srv:store' create a new entry in the ets table.

%% Storing RE and Funs
%% Creating simple fun when there's none provided...
%%
store([], Res, State) ->
ets:insert(State#state.re, { State#state.reindex, Res});
store([ { Regexp, Fun } | List ], Res, State) ->
{ok, Re } = treregex:compile(iolist_to_binary(Regexp), [extended]),
store(List, [ { Re, Fun } | Res ], State);
store([ Regexp | List ], Res, State) ->
{ok, Re } = treregex:compile(iolist_to_binary(Regexp), [extended]),
store(List, [ { Re, fun(_) -> false end} | Res ], State).

The 'tregex_srv:grep' just uses 'ets:foldl' to compute results:

handle_call({grep, Line}, _Node, State) ->
Requests = State#state.requests,
Grep = fun({_Reindex, ReList}, Acc) ->
[ exec(ReList, Line, []) | Acc]
end,
{reply, ets:foldl(Grep, [], State#state.re), State#state{ requests = Requests + 1} }.

%% exec, using a List of {Re, Funs}
exec([], _Line, Acc) ->
Acc;
exec([ { Re, Fun } | ReList ], Line, Acc) ->
case treregex:exec(Re, Line) of
{ok, Matches} ->
Fun(Matches),
exec(ReList, Line, [ Matches | Acc ]);

{error, nomatch} ->
exec(ReList, Line, Acc)
end;
exec([ _Any | ReList ], Line, Acc) ->
exec(ReList, Line, Acc).

The code is still young, but seems to work.

The main purpose here, is to be able to massively process lines of logs. I want to be able to
spawn multiple process on multiples nodes that will be able to extract valuable content from
various lines. This is the first step forward :-)

I may cleanup the 'grep' fun since it will returns empty list whenever a regexp didn't match anything from the supplied line...

I'm really excited to think that I will be able to use the 'gen_server:multi_call' with this module :)

Monday, October 22, 2007

LibTRE returning matching values

New version of libTRE driver, now exec returns also matching binaries:

Erlang (BEAM) emulator version 5.5.5 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.5.5 (abort with ^G)
1> erl_ddll:load_driver(code:priv_dir(treregex)++"/bin", "TRE_drv").
ok
2> {ok, RE} = treregex:compile(<<"([a-z]+)([0-9]+)">>, [extended]).
{ok,#Port<0.79>}
3> treregex:exec(RE, <<"this is a test9234 of blast">>).
{ok,[{10,18,<<"test9234">>},{10,14,<<"test">>},{14,18,<<"9234">>}]}
4>
4> treregex:exec(RE, <<"this is arolpghin39235 test9234 of blast">>).
{ok,[{8,22,<<"arolpghin39235">>},{8,17,<<"arolpghin">>},{17,22,<<"39235">>}]}
5>

Soon to be released !

Wednesday, October 17, 2007

LibTRE in Action, the approximative match

Here's a sample session showing some of the libTre features:

Erlang (BEAM) emulator version 5.5.5 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.5.5 (abort with ^G)
1> erl_ddll:load_driver(code:priv_dir(treregex)++"/bin", "TRE_drv").
ok
2> f(RE), {ok, RE} = treregex:compile(<<"fear">>, [extended]).
{ok,#Port<0.74>}
3> treregex:approx(RE, <<"fir">>, 0, []).
{error,nomatch}
4> treregex:approx(RE, <<"fir">>, 1, []).
{error,nomatch}
5> treregex:approx(RE, <<"fir">>, 2, []).
{ok,[{0,3}]}
6> treregex:approx(RE, <<"fir">>, 1, []).
{error,nomatch}
7> treregex:exec(RE, <<"fir">>).
{error,nomatch}
8> treregex:free(RE).
ok
9> f(RE), {ok, RE} = treregex:compile(<<"loubov">>, [extended]).
{ok,#Port<0.76>}
10> treregex:approx(RE, <<"love">>, 3, []).
{ok,[{0,3}]}
11> treregex:approx(RE, <<"to love">>, 0, []).
{error,nomatch}
12> treregex:approx(RE, <<"to love">>, 1, []).
{error,nomatch}
13> treregex:approx(RE, <<"to love">>, 2, []).
{error,nomatch}
14> treregex:approx(RE, <<"to love">>, 3, []).
{ok,[{0,6}]}
15> treregex:approx(RE, <<"aimer">>, 3, []).
{error,nomatch}
16> treregex:approx(RE, <<"aimour">>, 3, []).
{error,nomatch}
17> treregex:approx(RE, <<"amour">>, 3, []).
{error,nomatch}
18> treregex:approx(RE, <<"amour">>, 10, []).
{ok,[{1,6}]}

LibTRE, I finally got it working !

Hi !
I'm please to say that I finally managed the 'TRE_drv.c' code to work as expected. This time there's no more 'segfault'. The solution is to use only non conflicting function names:
  • regncomp and not regcomp
  • regnexec and not regexec
  • tree_free and not regfree
Since every posix Regex function will be used instead of their Tre equivalent, I was forced do find a special case for 'regfree'. There's no special regX function for 'regfree'.
Because regfree can be called at many places, when an erlang error occurs, or simply when we quit the shell; the function is trying to free the posix regex_t with a pointer to a TRE regex_t, so the crash is always there.
While looking at 'tre-internal.h' I've found the 'tre_free' function that will do the real job of freeing the TRE regex_t... So I've just declared as 'extern' this function directly into the TRE_drv.c file...
extern void tree_free(regex_t *preg);
static void tre_stop(ErlDrvData drv_data)
{
struct driver *d = (struct driver*) drv_data;

// if (d->compiled)
// regfree(&(d->re));

if (d->compiled)
tre_free(&d->re);

driver_free(d);
}


This time I've also added a call to the 'reganexec' function that's able to find approximate matches. The code is of course located into the 'tre_from_erlang' function. (this function gets called whenever erlang talks to the driver).

... snip ...
case APPROX:

/* <> */

flags = get_int32(buf + 4);
cost = get_int32(buf + 8);

memset(&amatches, 0, sizeof(amatches));
amatches.pmatch = matches;
amatches.nmatch = MAX_MATCHES;

regaparams_default(®aparams);
regaparams.max_cost = cost;


status = reganexec(&d->re, buf + 12, len - 12, &amatches, regaparams, flags);
if (status != 0) {
driver_send_status(d, status);
return;
}

driver_send_matches(d, matches, MAX_MATCHES);
break;
... snip ...


With this you're able to make things like this:

{ok, RE} = treregex:compile(<<"test">>, [extended]).
treregex:approx(RE, <<"this is a tast">>, 0, []).

the 'fun approx/4' has the 'cost' (an integer) as extra argument, this correspond to the cost of manipulation characters to find a match. You'll find more about this here.

Approximate pattern matching allows matches to be approximate, that is, allows the matches to be
close to the searched pattern under some measure of closeness. TRE uses the edit-distance measure
(also known as the Levenshtein distance) where characters can be inserted, deleted, or
substituted in the searched text in order to get an exact match. Each insertion, deletion, or
substitution adds the distance, or cost, of the match. TRE can report the matches which have a
cost lower than some given threshold value. TRE can also be used to search for matches with the
lowest cost.


Finally, now that the driver and the erlang module works well I'll upload it to various repositories...

Saturday, October 13, 2007

LibTre and Posix, the segfault explanation

You already know that I want to make a libTRE driver such as the RE posix driver... But what you may not know is that I've never lost this amount of time debugging things...
I've, for a long long time ago, called 'gdb' my friend to rescue me ...
Let me explain the problem: we want to call 'regcomp' and 'regexec' from the libTRE package. Take your time and reread this sentence:

We want to call 'regcomp' and 'regexec' from the libTRE package.

This seems really easy, but we want to make this from a shared library... A shared library that we will open at runtime.
And lauching our erlang shell make the ld process finds symbols addresses in various .so files for us automagically...

Since I'm using ubuntu, the posix regexp are located in the libc.so.6 library, so ld knows about regcomp and regexec before I can even call 'erl_ddll:load_driver/2'...

Loading the driver into the erlang vm from my freshly built .so file, don't really work as expected... 'regcomp' is still the one from the glibc, and not the one from the libTRE driver...

But 'regex_t' is the one from tre, and 'regcomp' is the one from posix... There definition of the regex_t struct isn't the same :p

The driver structure:

typedef struct _desc {
ErlDrvPort port;
ErlDrvTermData dport; /* the port identifier as ErlDrvTermData */
regex_t re;
regmatch_t pm[16];
int compiled;
} Desc;

Now the 'sizeof(regex_t)' from Tre is 8 and the posix is a lot more ... So when I call the 'regcomp' function like this:

switch(op) {
case COMPILE:
... snip ...
status = regcomp(&d->re, buf+8, flags);
d->compiled = 1;
... snip ...


The 'regmatch_t' is exactly overflowed by exactly
( sizeof (posix regex_t) - (2 * sizeof (tre regex_t)) ) 
bytes ...

This is the reason why I get a segfault (from my last post) when I try to call 'regexec'...

I'm now rewriting a simpler interface to libtre that will not have names that can collide with posix...

Wednesday, October 10, 2007

LibTre for Fast regular expression

While reading the almost famous article about regular expressions, I tried to use TRE.
Since TRE is posix Compliant, but unfortunately don't have any erlang driver I downloaded ''posregex'' and hack it a little to make it use TRE.

Now here's some experiment with it:

22> Init = fun() -> erl_ddll:load_driver(code:priv_dir(treregex)++"/bin", "TRE_drv") end.
#Fun
23> Init().
ok
24> f(List), List = treutils:build([<<"test">>, <<"toto">>, <<"[a-z][0-9]$">>, <<"^[a-zA-Z][a-zA-Z0-9_]+">>]).
[{<<"test">>,#Port<0.84>},
{<<"toto">>,#Port<0.85>},
{<<"[a-z][0-9]$">>,#Port<0.86>},
{<<"^[a-zA-Z][a-zA-Z0-9_]+">>,#Port<0.87>}]

25> treutils:exec(List, <<"alkjlskdjflskjglksjflakgjlkfgjl;dkgjklsdjglkdsjfglksd
jlkgjsdlkfgjlsdk fg989t9sgdkgj lkyrdy sjd gyrdsl;gkj test asl;dksdf">>).
{<<"alkjlskdjflskjglksjflakgjlkfgjl;dkgjklsdjglkdsjfglksdjlkgjsdlkfgjlsdk
fg989t9sgdkgj lkyrdy sjd gyrdsl;gkj test a"...>>,

[{ok,<<"^[a-zA-Z][a-zA-Z0-9_]+">>},{ok,<<"test">>}]}

26> treutils:exec(List, <<"10alkjlskdjflskjglksjflakgjlkfgjl;dkgjklsdjglkdsjfglksdjlkgjsdlkfgjlsdk
fg989t9sgdkgj lkyrdy sjd gyrdsl;gkj test asl;dksdf">>).
{<<"10alkjlskdjflskjglksjflakgjlkfgjl;dkgjklsdjglkdsjfglksdjlkgjsdlkfgjlsdk
fg989t9sgdkgj lkyrdy sjd gyrdsl;gkj test"...>>,

[{ok,<<"test">>}]}


I build a List of Port that are compiled regular expressions, then I iterate thru the list matching "Line".

Here's the TreUtils module (BTW, you can replace treregex with posregex if you want...)

-module(treutils).
-export([build/1, exec/2, destroy/1]).

build(List) ->
Fun = fun(X) ->
{ok, RE} = treregex:compile(X, [extended]),
{X, RE}
end,
lists:map(Fun, List).

destroy(List) ->
lists:foreach( fun({_Name, RE}) -> treregex:free(RE) end, List).

exec(List, Line) ->
exec(List, Line, []).

exec([], Line, Acc) ->
{Line, Acc};
exec([H|Rest], Line, Acc) ->
{Name, RE} = H,
case treregex:match(RE, Line) of
ok ->
exec(Rest, Line, [ {ok, Name} | Acc ]);

{error, nomatch} ->
exec(Rest, Line, Acc)
end.


Other libraries are also available.

Unfortunately I'm unable to make this module rock solid since it segfault if I ever call the 'exec' method two times... I think that there's a problem in the TRV_drv.c (heavily copied from the RE_drv.c) in the 'RE_from_erlang' function. The regexec call may garbage some of its internal...

typedef struct _desc {
ErlDrvPort port;
ErlDrvTermData dport; /* the port identifier as ErlDrvTermData */
regex_t re;
regmatch_t pm[16];
int compiled;
} Desc;

... snip ...

static void RE_from_erlang(ErlDrvData drv_data, char *buf, int len)
{
int status;
unsigned int op = get_int32(buf);
unsigned int flags = get_int32(buf+4);
Desc *d = (Desc*) drv_data;

switch(op) {

... snip ...

case EXEC:
status = regexec(&d->re, buf+8, (size_t) 16, &d->pm[0], flags);
if (status != 0) {
driver_send_status(d, status);
return;
}
driver_send_pm(d);
break;


I hope I'll come back soon with good news, since this TRE library looks very very promising...
If anyone have any clue ;p, please comment !

Sticky