Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

- add log action to <match> (TSUN-94)

- add userid dyn_variable (TSUN-95)

SVN Revision: 953
  • Loading branch information...
commit 17a817873469b0fe877b6b75e8b3680d51688ead 1 parent baf5694
Nicolas Niclausse nniclausse authored
6 CONTRIBUTORS
View
@@ -20,12 +20,14 @@ o J
o Jason Tucker: Solaris testing and fixes, jabber patches (sip_digest,
roster and presence enhancements, bidi support for presence:subscribe ).
-o Pablo Polvorin: LDAP plugin, set_dynvars, xpath search for html.
+o Pablo Polvorin: LDAP plugin, set_dynvars, xpath search for html,
+ for/repeat loop, dynvars_api, hibernate.
o Gregoire Reboul: MySQL plugin.
+o Dimitri Fontaine: SNMP & postgresql testing, patch for snmp, tsung-plotter
+
o Jonathan Bresler: Jabber testing and bug reporting
-o Dimitri Fontaine: SNMP & postgresql testing, patch for snmp.
o Gordon Guthrie: tips for ssh setup on Suse
o Romain Lenglet: Suggestions for ts_os_mon
o Johann Messner: Bug reports
1  include/ts_profile.hrl
View
@@ -92,6 +92,7 @@
% all the response to do pattern matching)
session, % record of session status; depends on 'clienttype'
datasize=0,
+ id, % user id
size_mon_thresh=?size_mon_thresh, % if rcv data is > to this, update stats
dyndata=[], % persistent data dynamically added during the
% session (Cookies for examples)
12 src/test/ts_test_config.erl
View
@@ -34,12 +34,12 @@ config_get_session_test() ->
ts_user_server:start([]),
ts_config_server:start_link(["/tmp"]),
ok = ts_config_server:read_config("./examples/http_setdynvars.xml"),
- {ok, {Session,IP,Server} } = ts_config_server:get_next_session("localhost"),
+ {ok, {Session,IP,Server,1} } = ts_config_server:get_next_session("localhost"),
?assertEqual(1, Session#session.id).
config_get_session_size_test() ->
myset_env(),
- {ok, {Session,IP,Server} } = ts_config_server:get_next_session("localhost"),
+ {ok, {Session,IP,Server,2} } = ts_config_server:get_next_session("localhost"),
?assertEqual(13, Session#session.size).
@@ -57,13 +57,13 @@ read_config_thinkfirst_test() ->
config_minmax_test() ->
myset_env(),
- {ok, {Session,IP,Server} } = ts_config_server:get_next_session("localhost"),
+ {ok, {Session,IP,Server,3} } = ts_config_server:get_next_session("localhost"),
Id = Session#session.id,
?assertMatch({thinktime,{range,2000,4000}}, ts_config_server:get_req(Id,7)).
config_minmax2_test() ->
myset_env(),
- {ok, {Session,IP,Server} } = ts_config_server:get_next_session("localhost"),
+ {ok, {Session,IP,Server,4} } = ts_config_server:get_next_session("localhost"),
Id = Session#session.id,
{thinktime, Req} = ts_config_server:get_req(Id,7),
Think=ts_client:set_thinktime(Req),
@@ -75,7 +75,7 @@ config_minmax2_test() ->
config_thinktime_test() ->
myset_env(),
ok = ts_config_server:read_config("./examples/thinks.xml"),
- {ok, {Session,IP,Server} } = ts_config_server:get_next_session("localhost"),
+ {ok, {Session,IP,Server,5} } = ts_config_server:get_next_session("localhost"),
Id = Session#session.id,
{thinktime, Req=2000} = ts_config_server:get_req(Id,5),
{thinktime, 2000} = ts_config_server:get_req(Id,7),
@@ -89,7 +89,7 @@ config_thinktime_test() ->
config_thinktime2_test() ->
myset_env(),
ok = ts_config_server:read_config("./examples/thinks2.xml"),
- {ok, {Session,IP,Server} } = ts_config_server:get_next_session("localhost"),
+ {ok, {Session,IP,Server,6} } = ts_config_server:get_next_session("localhost"),
Id = Session#session.id,
{thinktime, Req} = ts_config_server:get_req(Id,5),
Ref=ts_client:set_thinktime(Req),
35 src/test/ts_test_match.erl
View
@@ -16,89 +16,92 @@
-define(MAX_COUNT,42).
-define(COUNT,5).
+-define(USER_ID,2).
+-define(SESSION_ID,1).
+-define(COUNTS,{5,42,2,1}).
test()->
ok.
match_abort_ok_test() ->
myset_env(),
Data="C'est n'est pas une chaine de caractere",
- ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=abort, 'when'=match}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=abort, 'when'=match}],Data, ?COUNTS)).
match_abort_nok_test() ->
myset_env(),
Data="Ceci est une Erreur",
- ?assertMatch(0, ts_search:match([#match{regexp="Erreur", do=abort, 'when'=match}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(0, ts_search:match([#match{regexp="Erreur", do=abort, 'when'=match}],Data, ?COUNTS)).
nomatch_abort_ok_test() ->
myset_env(),
Data="C'est n'est pas une chaine de caractere",
- ?assertMatch(0, ts_search:match([#match{regexp="Erreur", do=abort, 'when'=nomatch}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(0, ts_search:match([#match{regexp="Erreur", do=abort, 'when'=nomatch}],Data, ?COUNTS)).
nomatch_abort_nok_test() ->
myset_env(),
Data="Ceci est une Erreur",
- ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=abort, 'when'=nomatch}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=abort, 'when'=nomatch}],Data, ?COUNTS)).
nomatch_continue_ok_test() ->
myset_env(),
Data="C'est n'est pas une chaine de caractere",
- ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=continue, 'when'=nomatch}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=continue, 'when'=nomatch}],Data, ?COUNTS)).
nomatch_continue_nok_test() ->
myset_env(),
Data="Ceci est une Erreur",
- ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=continue, 'when'=nomatch}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=continue, 'when'=nomatch}],Data, ?COUNTS)).
match_continue_ok_test() ->
myset_env(),
Data="C'est n'est pas une chaine de caractere",
- ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=continue, 'when'=match}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=continue, 'when'=match}],Data, ?COUNTS)).
match_continue_nok_test() ->
myset_env(),
Data="Ceci est une Erreur",
- ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=continue, 'when'=match}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=continue, 'when'=match}],Data, ?COUNTS)).
nomatch_loop_ok_test() ->
myset_env(),
Data="C'est n'est pas une chaine de caractere",
- ?assertMatch(?COUNT+1, ts_search:match([#match{regexp="Erreur", do=loop, max_loop=?COUNT, loop_back=0, sleep_loop=1,'when'=nomatch}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?COUNT+1, ts_search:match([#match{regexp="Erreur", do=loop, max_loop=?COUNT, loop_back=0, sleep_loop=1,'when'=nomatch}],Data, ?COUNTS)).
nomatch_loop_nok_test() ->
myset_env(),
Data="Ceci est une Erreur",
- ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=loop, max_loop=?COUNT, loop_back=0, sleep_loop=1,'when'=nomatch}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=loop, max_loop=?COUNT, loop_back=0, sleep_loop=1,'when'=nomatch}],Data, ?COUNTS)).
match_loop_ok_test() ->
myset_env(),
Data="C'est n'est pas une chaine de caractere",
- ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=loop, max_loop=?COUNT, loop_back=0, sleep_loop=1,'when'=match}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=loop, max_loop=?COUNT, loop_back=0, sleep_loop=1,'when'=match}],Data, ?COUNTS)).
match_loop_nok_test() ->
myset_env(),
Data="Ceci est une Erreur",
- ?assertMatch(?COUNT+1, ts_search:match([#match{regexp="Erreur", do=loop, max_loop=?COUNT, loop_back=0, sleep_loop=1, 'when'=match}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?COUNT+1, ts_search:match([#match{regexp="Erreur", do=loop, max_loop=?COUNT, loop_back=0, sleep_loop=1, 'when'=match}],Data, ?COUNTS)).
nomatch_restart_ok_test() ->
myset_env(),
Data="C'est n'est pas une chaine de caractere",
- ?assertMatch(?MAX_COUNT, ts_search:match([#match{regexp="Erreur", do=restart, max_restart=?COUNT,'when'=nomatch}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?MAX_COUNT, ts_search:match([#match{regexp="Erreur", do=restart, max_restart=?COUNT,'when'=nomatch}],Data, ?COUNTS)).
nomatch_restart_nok_test() ->
myset_env(),
Data="Ceci est une Erreur",
- ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=restart, max_restart=?COUNT,'when'=nomatch}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=restart, max_restart=?COUNT,'when'=nomatch}],Data, ?COUNTS)).
match_restart_ok_test() ->
myset_env(),
Data="C'est n'est pas une chaine de caractere",
- ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=restart, max_restart=?COUNT,'when'=match}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?COUNT, ts_search:match([#match{regexp="Erreur", do=restart, max_restart=?COUNT,'when'=match}],Data, ?COUNTS)).
match_restart_nok_test() ->
myset_env(),
Data="Ceci est une Erreur",
- ?assertMatch(?MAX_COUNT, ts_search:match([#match{regexp="Erreur", do=restart, max_restart=?COUNT, 'when'=match}],Data, {?COUNT,?MAX_COUNT})).
+ ?assertMatch(?MAX_COUNT, ts_search:match([#match{regexp="Erreur", do=restart, max_restart=?COUNT, 'when'=match}],Data, ?COUNTS)).
myset_env()->
myset_env(0).
8 src/test/ts_test_search.erl
View
@@ -181,6 +181,14 @@ parse_extract_fun2_test() ->
Data="/stuff/%%ts_test_search:namespace%%/%%ts_test_search:marketplace%%/%%ts_test_search:sessionBucket%%/01/2000?keyA1=dataA1&amp;keyB1=dataB1",
?assertMatch("/stuff/namespace2/6/91/01/2000?keyA1=dataA1&amp;keyB1=dataB1", ts_search:subst(Data,[])).
+parse_subst_var_fun_test() ->
+ myset_env(),
+ Data=?FORMDATA,
+ StrName="jsf_tree_64",
+ Regexp = ?DEF_REGEXP_DYNVAR_BEGIN++ StrName ++?DEF_REGEXP_DYNVAR_END,%'
+ [{Name,Value}] = ts_search:parse_dynvar([{regexp, 'jsf_tree_64', Regexp }],list_to_binary(Data)),
+ ?assertMatch("H4sIAAAAAAAAAK1VS2/TQBBeo+kalCKAA-MSFT", ts_search:subst("%%_jsf_tree_64%%-%%ts_test_search:new%%",[{Name,Value}])).
+
dynvars_urandom_test() ->
myset_env(),
?assertMatch(["qxvmvtglimieyhemzlxc"],ts_client:set_dynvars(urandom,{string,20},[toto],[])).
19 src/tsung/ts_client.erl
View
@@ -49,13 +49,15 @@
%%% API
%%%----------------------------------------------------------------------
+%% @spec start(Opts::{Session::#session{},IP::tuple(),Server::#server{}},
+%% Id::integer()) -> {ok, Pid::pid()} | ignore | {error, Error::term()}
%% @doc Start a new session
start(Opts) ->
?DebugF("Starting with opts: ~p~n",[Opts]),
gen_fsm:start_link(?MODULE, Opts, []).
%%----------------------------------------------------------------------
-%% next/1
+%% @spec next({pid()}) -> ok
%% @doc Purpose: continue with the next request (used for global ack)
%% @end
%%----------------------------------------------------------------------
@@ -79,12 +81,14 @@ init({#session{id = SessionId,
hibernate = Hibernate,
proto_opts = ProtoOpts,
size = Count,
- type = CType}, IP, Server}) ->
+ type = CType}, IP, Server,Id}) ->
?DebugF("Init ... started with count = ~p~n",[Count]),
ts_utils:init_seed(),
?DebugF("Get dynparams for ~p~n",[CType]),
DynData = CType:init_dynparams(),
+ NewDynVars = ts_dynvars:set(tsung_userid,Id,DynData#dyndata.dynvars),
+ NewDynData = DynData#dyndata{dynvars=NewDynVars},
StartTime= now(),
ts_mon:newclient({self(), StartTime}),
set_thinktime(?short_timeout),
@@ -101,9 +105,10 @@ init({#session{id = SessionId,
proto_opts = ProtoOpts,
count = Count,
ip = IP,
+ id = Id,
hibernate = Hibernate,
maxcount = Count,
- dyndata = DynData
+ dyndata = NewDynData
}}.
%%--------------------------------------------------------------------
@@ -350,7 +355,8 @@ handle_next_action(State) ->
%%----------------------------------------------------------------------
%% @spec set_dynvars (Type::erlang|random|urandom|file, Args::tuple(),
%% Variables::list(), DynData::#dyndata{}) -> list()
-%% @doc setting the value of several dynamic variables at once.
+%% @doc setting the value of several dynamic variables at once.
+%% @end
%%----------------------------------------------------------------------
set_dynvars(erlang,{Module,Callback},_Vars,DynData) ->
Module:Callback({self(),DynData#dyndata.dynvars});
@@ -733,7 +739,8 @@ handle_data_msg(Data,State=#state_rcv{request=Req,clienttype=Type,maxcount=MaxCo
true ->
?DebugF("Response done:~p~n", [NewState#state_rcv.datasize]),
{PageTimeStamp, DynVars} = update_stats(NewState#state_rcv{buffer=NewBuffer}),
- NewCount = ts_search:match(Req#ts_request.match, NewBuffer, {NewState#state_rcv.count, MaxCount}),
+ NewCount = ts_search:match(Req#ts_request.match, NewBuffer,
+ {NewState#state_rcv.count, MaxCount, NewState#state_rcv.session_id, NewState#state_rcv.id}),
NewDynVars=ts_dynvars:merge(DynVars,(NewState#state_rcv.dyndata)#dyndata.dynvars),
NewDynData=(NewState#state_rcv.dyndata)#dyndata{dynvars=NewDynVars},
case Close of
@@ -799,7 +806,7 @@ handle_data_msg(Data, State=#state_rcv{request=Req, maxcount= MaxCount}) ->
DataSize = size(Data),
{PageTimeStamp, DynVars} = update_stats(State#state_rcv{datasize=DataSize,
buffer=NewBuffer}),
- NewCount = ts_search:match(Req#ts_request.match, NewBuffer, {State#state_rcv.count,MaxCount}),
+ NewCount = ts_search:match(Req#ts_request.match, NewBuffer, {State#state_rcv.count,MaxCount,State#state_rcv.id,State#state_rcv.id}),
NewDynVars=ts_dynvars:merge(DynVars,(State#state_rcv.dyndata)#dyndata.dynvars),
NewDynData=(State#state_rcv.dyndata)#dyndata{dynvars=NewDynVars},
{State#state_rcv{ack_done = true, buffer= NewBuffer, dyndata = NewDynData,
7 src/tsung/ts_client_sup.erl
View
@@ -50,10 +50,9 @@ start_child(Profile) ->
%%%----------------------------------------------------------------------
%%--------------------------------------------------------------------
-%% Func: active_clients/0
-%% Returns: [ Client ]
-%% Description: returns the list of all active children on this beam's
-%% client supervisor.
+%% @spec active_clients() -> [tuple()]
+%% @doc returns the list of all active children on this beam's
+%% client supervisor. @end
%%--------------------------------------------------------------------
active_clients()->
length(supervisor:which_children(?MODULE)).
45 src/tsung/ts_search.erl
View
@@ -74,7 +74,8 @@ extract_variable([],_DynVar,Acc,_) ->
extract_variable([$%,$%|Tail], DynVar, Acc, Var) ->
VarName = list_to_atom(lists:reverse(Var)),
case ts_dynvars:lookup(VarName,DynVar) of
- {ok, Result} ->
+ {ok, ResultTmp} ->
+ Result=ts_utils:term_to_list(ResultTmp),
?DebugF("found value ~p for name ~p~n",[Result,VarName]),
subst(Tail, DynVar,lists:reverse(Result) ++ Acc);
false ->
@@ -108,21 +109,21 @@ extract_function([H|Tail],DynVar, Acc, Mod, Fun) ->
extract_function(Tail, DynVar, Acc, Mod, [H|Fun]).
%%----------------------------------------------------------------------
-%% @spec match(Match::#match{}, Data::binary() | list, Counts::integer())
-%% -> Count::integer()
+%% @spec match(Match::#match{}, Data::binary() | list, Counts::integer(),
+%% Id::integer())-> Count::integer()
%% @doc search for regexp in Data; send result to ts_mon
%% @end
%%----------------------------------------------------------------------
-match([], _Data, {Count, _MaxC}) -> Count;
+match([], _Data, {Count, _MaxC, _SessionId, _UserId}) -> Count;
match(Match, Data, Counts) when is_binary(Data)->
?DebugF("Matching Data size ~p~n",[size(Data)]),
- match(Match, binary_to_list(Data), Counts, []);
-match(Match, Data, Counts) ->
- match(Match, Data, Counts, []).
+ match(Match, binary_to_list(Data), Counts);
+match(Match, Data, {Count,MaxC,SessionId,UserId}) when is_list(Data) ->
+ match(Match, Data, {Count,MaxC,SessionId,UserId}, []).
-%% @spec match(Match::#match{}, Data::binary() | list(), Counts::integer(), Stats::list())
-%% -> Count::integer()
-match([], _Data, {Count, _MaxC}, Stats) ->
+%% @spec match(Match::#match{}, Data::binary() | list(), Count::tuple(),
+%% Stats::list()) -> Count::integer()
+match([], _Data, {Count, _MaxC, _SessionId,_UserId}, Stats) ->
%% all matches done, add stats, and return Count unchanged (continue)
ts_mon:add(Stats),
Count;
@@ -134,7 +135,7 @@ match([Match=#match{regexp=RegExp, do=Action, 'when'=When}| Tail], String, Count
When -> % nomatch
?LOGF("Bad Match (regexp=~p) do=~p~n",[RegExp, Action], ?INFO),
setcount(Match, Counts, [{count, nomatch} | Stats]);
- {match,_, _} ->
+ {match,_, _} -> % match but when=nomatch
?LOGF("Ok Match (regexp=~p)~n",[RegExp], ?INFO),
case Action of
loop -> put(loop_count, 0);
@@ -142,7 +143,7 @@ match([Match=#match{regexp=RegExp, do=Action, 'when'=When}| Tail], String, Count
_ -> ok
end,
match(Tail, String, Counts, [{count, match} | Stats]);
- nomatch ->
+ nomatch -> % nomatch but when=match
?LOGF("Bad Match (regexp=~p)~n",[RegExp], ?INFO),
case Action of
loop -> put(loop_count, 0);
@@ -163,27 +164,31 @@ match([Match=#match{regexp=RegExp, do=Action, 'when'=When}| Tail], String, Count
%% - if restart is true, we must start again the whole session, set count to MaxCount
%% - if stop is true, set count to 0
%%----------------------------------------------------------------------
-setcount(#match{do=continue}, {Count, _MaxC}, Stats)->
+setcount(#match{do=continue}, {Count, _MaxC, _SessionId, _UserId}, Stats)->
ts_mon:add(Stats),
Count;
-setcount(#match{do=restart, max_restart=MaxRestart}, {_Count, MaxC}, Stats)->
+setcount(#match{do=log}, {Count, MaxC, SessionId, UserId}, Stats)->
+ ts_mon:add_match(Stats,{UserId,SessionId,MaxC-Count}),
+ Count;
+setcount(#match{do=restart, max_restart=MaxRestart}, {Count, MaxC,SessionId,UserId}, Stats)->
CurRestart = get(restart_count),
+ Ids={UserId,SessionId,MaxC-Count},
?LOGF("Restart on (no)match ~p~n",[CurRestart], ?INFO),
case CurRestart of
undefined ->
put(restart_count,1),
- ts_mon:add([{count, match_restart} | Stats]),
+ ts_mon:add_match([{count, match_restart} | Stats],Ids),
MaxC ;
Val when Val > MaxRestart ->
?LOG("Max restart reached, abort ! ~n", ?WARN),
- ts_mon:add([{count, match_restart_abort} | Stats]),
+ ts_mon:add_match([{count, match_restart_abort} | Stats],Ids),
0;
Val ->
put(restart_count, Val +1),
- ts_mon:add([{count, match_restart} | Stats]),
+ ts_mon:add_match([{count, match_restart} | Stats],Ids),
MaxC
end;
-setcount(#match{do=loop,loop_back=Back,max_loop=MaxLoop,sleep_loop=Sleep},{Count,_MaxC},Stats)->
+setcount(#match{do=loop,loop_back=Back,max_loop=MaxLoop,sleep_loop=Sleep},{Count,_MaxC,_SessionId,_UserId},Stats)->
CurLoop = get(loop_count),
?LOGF("Loop on (no)match ~p~n",[CurLoop], ?INFO),
ts_mon:add([{count, match_loop} | Stats]),
@@ -201,8 +206,8 @@ setcount(#match{do=loop,loop_back=Back,max_loop=MaxLoop,sleep_loop=Sleep},{Count
timer:sleep(Sleep),
Count + 1 + Back
end;
-setcount(#match{do=abort}, _, Stats) ->
- ts_mon:add([{count, match_stop} | Stats]),
+setcount(#match{do=abort}, {Count,MaxC,SessionId,UserId}, Stats) ->
+ ts_mon:add_match([{count, match_stop} | Stats],{SessionId,UserId,MaxC-Count}),
0.
%%----------------------------------------------------------------------
21 src/tsung/ts_session_cache.erl
View
@@ -35,7 +35,7 @@
%%--------------------------------------------------------------------
%% External exports
--export([start/0, get_req/2, get_user_agent/0, add/1]).
+-export([start/0, get_req/2, get_user_agent/0, add/1, add_match/2]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
@@ -46,7 +46,8 @@
table, % ets table
hit =0.0, % number of hits
total=0.0, % total number of requests
- stats=[] % cache stats msgs
+ stats=[], % cache stats msgs
+ match=[] % cache match logs
}).
-define(DUMP_STATS_INTERVAL, 500). % in milliseconds
@@ -85,6 +86,11 @@ get_user_agent()->
add(Data) ->
gen_server:cast(?MODULE, {add, Data}).
+%% @spec add_match(Data::list(),{UserId::integer(),SessionId::integer(),RequestId::integer(),
+%% TimeStamp::tuple()}) -> ok
+add_match(Data,{UserId,SessionId,RequestId,TimeStamp}) ->
+ gen_server:cast(?MODULE, {add_match, Data, {UserId,SessionId,RequestId,TimeStamp}}).
+
%%====================================================================
%% Server functions
%%====================================================================
@@ -173,6 +179,10 @@ handle_cast({add, Data}, State=#state{stats=List}) when is_list(Data) ->
{noreply, State#state{stats = lists:append(Data, List)} };
handle_cast({add, Data}, State=#state{stats=List}) when is_tuple(Data) ->
{noreply, State#state{stats = lists:append([Data], List)} };
+handle_cast({add_match, Data=[First|_Tail],{UserId,SessionId,RequestId,TimeStamp}},
+ State=#state{stats=List, match=MatchList})->
+ NewMatchList=lists:append([{UserId,SessionId,RequestId,TimeStamp,First}], MatchList),
+ {noreply, State#state{stats = lists:append(Data, List), match = NewMatchList}};
handle_cast(_Msg, State) ->
{noreply, State}.
@@ -188,10 +198,11 @@ handle_info({timeout, _Ref, dump_stats}, State = #state{stats =[]}) ->
erlang:start_timer(?DUMP_STATS_INTERVAL, self(), dump_stats ),
{noreply, State};
-handle_info({timeout, _Ref, dump_stats}, State =#state{stats= List}) ->
- ts_stats_mon:add(List),
+handle_info({timeout, _Ref, dump_stats}, State =#state{stats= Stats, match=MatchList}) ->
+ ts_stats_mon:add(Stats),
+ ts_match_logger:add(MatchList),
erlang:start_timer(?DUMP_STATS_INTERVAL, self(), dump_stats ),
- {noreply, State#state{stats=[]}};
+ {noreply, State#state{stats=[],match=[]}};
handle_info(_Info, State) ->
{noreply, State}.
17 src/tsung/ts_utils.erl
View
@@ -39,9 +39,10 @@
foreach_parallel/2, spawn_par/3, inet_setopts/3, resolve/2,
stop_all/2, stop_all/3, stop_all/4, join/2, split2/2, split2/3,
make_dir_rec/1, is_ip/1, from_https/1, to_https/1, keymax/2,
- check_sum/3, check_sum/5, clean_str/1, file_to_list/1,
+ check_sum/3, check_sum/5, clean_str/1, file_to_list/1, term_to_list/1,
decode_base64/1, encode_base64/1, to_lower/1, release_is_newer_or_eq/1,
- randomstr/1,urandomstr/1,urandomstr_noflat/1, eval/1, list_to_number/1
+ randomstr/1,urandomstr/1,urandomstr_noflat/1, eval/1, list_to_number/1,
+ time2sec/1
]).
level2int("debug") -> ?DEB;
@@ -146,7 +147,9 @@ init_seed()->
%% Purpose: returns unix like elapsed time in sec
%%----------------------------------------------------------------------
now_sec() ->
- {MSec, Seconds, _} = now(),
+ time2sec(now()).
+
+time2sec({MSec, Seconds, _}) ->
Seconds+1000000*MSec.
%%----------------------------------------------------------------------
@@ -666,3 +669,11 @@ list_to_number(Number) ->
list_to_float(Number)
end.
+term_to_list(I) when is_integer(I)->
+ integer_to_list(I);
+term_to_list(I) when is_atom(I)->
+ atom_to_list(I);
+term_to_list(I) when is_list(I)->
+ I;
+term_to_list(I) when is_float(I)->
+ float_to_list(I).
12 src/tsung_controller/ts_config_server.erl
View
@@ -62,6 +62,7 @@
-record(state, {config,
logdir,
+ users=1, % userid (incremental counter)
start_date, %
hostname, % controller hostname
last_beam_id = 0, % last tsung beam id (used to set nodenames)
@@ -140,9 +141,10 @@ get_monitor_hosts()->
gen_server:call({global,?MODULE},{get_monitor_hosts}).
%%--------------------------------------------------------------------
-%% Function: get_next_session/0
-%% Description: choose randomly a session
-%% Returns: {ok, Session ID, Session Size (integer), IP (tuple)}
+%% @spec get_next_session(Host::string())-> {ok, SessionId::integer(),
+%% SessionSize::integer(),IP::tuple(), UserId::integer()}
+%% @doc Choose randomly a session
+%% @end
%%--------------------------------------------------------------------
get_next_session(Host)->
gen_server:call({global, ?MODULE},{get_next_session, Host}).
@@ -239,7 +241,7 @@ handle_call({get_user_agents}, _From, State) ->
end;
%% get a new session id and an ip for the given node
-handle_call({get_next_session, HostName}, _From, State) ->
+handle_call({get_next_session, HostName}, _From, State=#state{users=Users}) ->
Config = State#state.config,
{value, Client} = lists:keysearch(HostName, #client.host, Config#config.clients),
@@ -250,7 +252,7 @@ handle_call({get_next_session, HostName}, _From, State) ->
case choose_session(Config#config.sessions) of
{ok, Session=#session{id=Id}} ->
?LOGF("Session ~p choosen~n",[Id],?INFO),
- {reply, {ok, {Session, IP, Server}}, State};
+ {reply, {ok, {Session, IP, Server,Users}}, State#state{users=Users+1}};
Other ->
{reply, {error, Other}, State}
end;
4 src/tsung_controller/ts_controller_sup.erl
View
@@ -61,6 +61,8 @@ init([LogDir]) ->
worker, [ts_mon]},
Stats_Mon = {ts_stats_mon, {ts_stats_mon, start, []}, transient, 2000,
worker, [ts_stats_mon]},
+ Match_Log = {ts_match_logger, {ts_match_logger, start, [LogDir]}, transient, 2000,
+ worker, [ts_match_logger]},
Os_Mon = {ts_os_mon, {ts_os_mon, start, []}, transient, 2000,
worker, [ts_os_mon]},
Timer = {ts_timer, {ts_timer, start, [?config(nclients)]}, transient, 2000,
@@ -72,7 +74,7 @@ init([LogDir]) ->
?config(nclients)]]},
transient, 2000, worker, [ts_user_server]},
{ok,{{one_for_one,?retries,10},
- [Config, Mon, Stats_Mon, Timer, Msg, User, Os_Mon]}}.
+ [Config, Mon, Stats_Mon, Match_Log, Timer, Msg, User, Os_Mon]}}.
%%%----------------------------------------------------------------------
%%% Internal functions
177 src/tsung_controller/ts_match_logger.erl
View
@@ -0,0 +1,177 @@
+%%%
+%%% Copyright (C) 2008 Nicolas Niclausse
+%%%
+%%% This program is free software; you can redistribute it and/or modify
+%%% it under the terms of the GNU General Public License as published by
+%%% the Free Software Foundation; either version 2 of the License, or
+%%% (at your option) any later version.
+%%%
+%%% This program is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%%% GNU General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License
+%%% along with this program; if not, write to the Free Software
+%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+%%%
+%%% In addition, as a special exception, you have the permission to
+%%% link the code of this program with any library released under
+%%% the EPL license and distribute linked combinations including
+%%% the two.
+
+
+%%----------------------------------------------------------------------
+%% @copyright 2008 Nicolas Niclausse
+%% @author Nicolas Niclausse <nicolas@niclux.org>
+%% @since 1.3.1 , 19 Nov 2008
+%% @doc log match entries @end
+%% ----------------------------------------------------------------------
+
+-module(ts_match_logger).
+-author('nicolas@niclux.org').
+-vc('$Id: ts_mon.erl 774 2007-11-20 09:36:13Z nniclausse $ ').
+
+-behaviour(gen_server).
+
+-include("ts_profile.hrl").
+-include("ts_config.hrl").
+
+-define(DELAYED_WRITE_SIZE,524288). % 512KB
+-define(DELAYED_WRITE_DELAY,5000). % 5 sec
+
+%% External exports, API
+-export([start/1, stop/0, add/1 ]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
+ code_change/3]).
+
+-record(state, {filename, % log filename
+ level, % type of backend: text|rrdtool|fullstats
+ fd % file descriptor
+ }).
+
+%%%----------------------------------------------------------------------
+%%% API
+%%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%% @spec start() -> ok | throw({error, Reason})
+%% @doc Start the monitoring process
+%% @end
+%%----------------------------------------------------------------------
+start(LogDir) ->
+ ?LOG("starting match logger, global ~n",?NOTICE),
+ gen_server:start_link({global, ?MODULE}, ?MODULE, [LogDir], []).
+
+stop() ->
+ gen_server:cast({global, ?MODULE}, {stop}).
+
+%%----------------------------------------------------------------------
+%% @spec add(Data::list()| {UserId::integer(),SessionId::integer(),
+%% RequestId::integer(),TimeStamp::tuple(),{count, Val::atom()}}) -> ok
+%% @doc log match entries
+%% @end
+%%----------------------------------------------------------------------
+add(Data) ->
+ gen_server:cast({global, ?MODULE}, {add, Data}).
+
+
+%%%----------------------------------------------------------------------
+%%% Callback functions from gen_server
+%%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%%----------------------------------------------------------------------
+init([LogDir]) ->
+ ?LOG("starting match logger~n",?NOTICE),
+ Base = filename:basename(?config(match_log_file)),
+ Filename = filename:join(LogDir, Base),
+ case file:open(Filename,[write, {delayed_write, ?DELAYED_WRITE_SIZE, ?DELAYED_WRITE_DELAY}]) of
+ {ok, Fd} ->
+ ?LOG("starting match logger~n",?NOTICE),
+ {ok, #state{ fd = Fd,
+ filename = Filename
+ }};
+ {error, Reason} ->
+ ?LOGF("Can't open match log file! ~p~n",[Reason], ?ERR),
+ {stop, Reason}
+ end.
+
+%%----------------------------------------------------------------------
+%% Func: handle_call/3
+%% Returns: {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} | (terminate/2 is called)
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+handle_call(Request, _From, State) ->
+ ?LOGF("Unknown call ~p !~n",[Request],?ERR),
+ Reply = ok,
+ {reply, Reply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_cast/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+handle_cast({add, List}, State) when is_list(List)->
+ lists:foreach(fun(X)-> log(X,State#state.fd) end, List),
+ {noreply,State};
+
+handle_cast({add, Data}, State) when is_tuple(Data)->
+ log(Data,State#state.fd),
+ {noreply,State};
+
+handle_cast({stop}, State) ->
+ {stop, normal, State};
+
+handle_cast(Msg, State) ->
+ ?LOGF("Unknown msg ~p !~n",[Msg], ?WARN),
+ {noreply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_info/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: terminate/2
+%% Purpose: Shutdown the server
+%% Returns: any (ignored by gen_server)
+%%----------------------------------------------------------------------
+terminate(Reason, State) ->
+ ?LOGF("stoping match logger (~p)~n",[Reason],?NOTICE),
+ file:close(State#state.fd),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Convert process state when code is changed
+%% Returns: {ok, NewState, NewStateData}
+%%--------------------------------------------------------------------
+code_change(_OldVsn, StateData, _Extra) ->
+ {ok, StateData}.
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+log({UserId,SessionId,RequestId,TimeStamp,{count, Val}}, File) ->
+ TS=ts_utils:time2sec(TimeStamp),
+ io:format(File,"~B ~B ~B ~B ~p~n",[TS,UserId,SessionId,RequestId,Val]).
+
+
15 src/tsung_controller/ts_mon.erl
View
@@ -43,7 +43,8 @@
%% External exports
-export([start/1, stop/0, newclient/1, endclient/1, sendmes/1, add/2,
- start_clients/1, abort/0, status/0, rcvmes/1, add/1, dumpstats/0
+ start_clients/1, abort/0, status/0, rcvmes/1, add/1, dumpstats/0,
+ add_match/2
]).
%% gen_server callbacks
@@ -76,14 +77,15 @@
%%----------------------------------------------------------------------
-%% FUNCTION start/0
-%% PURPOSE Start the monitoring process
-%% RETURN VALUE ok | throw({error, Reason})
+%% @spec start()-> {ok, Pid::pid()} | ignore | {error, Error::term()}
+%% @doc Start the monitoring process
+%% @end
%%----------------------------------------------------------------------
start(LogDir) ->
?LOG("starting monitor, global ~n",?NOTICE),
gen_server:start_link({global, ?MODULE}, ?MODULE, [LogDir], []).
+%% @spec start_clients({Machines::term(), Dump::string(), BackEnd::atom()}) -> ok
start_clients({Machines, Dump, BackEnd}) ->
gen_server:call({global, ?MODULE}, {start_logger, Machines, Dump, BackEnd},
infinity).
@@ -93,9 +95,14 @@ stop() ->
add(nocache,Data) ->
gen_server:cast({global, ?MODULE}, {add, Data}).
+
add(Data) ->
ts_session_cache:add(Data).
+add_match(Data,{UserId,SessionId,RequestId}) ->
+ TimeStamp=now(),
+ ts_session_cache:add_match(Data,{UserId,SessionId,RequestId,TimeStamp}).
+
status() ->
gen_server:call({global, ?MODULE}, {status}).
49 src/tsung_controller/ts_user_server.erl
View
@@ -74,28 +74,21 @@ reset(NFin)->
get_id()->
gen_server:call({global, ?MODULE }, get_id).
-%% return a unique id. use the pid and the node name to set an id.
-get_unique_id({Pid, _DynData})->
- ID = pid_to_list(Pid),
- [ID1,ID2,ID3] = string:tokens(ID, [$<,$.,$>]),
- %% here we assume that the node name is tsungXX@whatever
- NodeId = case string:tokens(atom_to_list(node()),[$@,$g]) of
- ["tsun", "_controller"|_] ->
- "0"; % when the launcher is run on the controller
- ["tsun", Id|_] ->
- Id
- end,
- %% all ID's must be < 65536 !
- ?DebugF("got unique id from ~p",[[ID1, ID3, NodeId, ID2]]),
- {ok, Int} = list_to_id([ID1, ID3, NodeId, ID2],65536),
- ?DebugF("id=~p",[Int]),
- [integer_to_list(Int)].
-
+%% return a unique id. deprecated since tsung_userid dyn var exists
+get_unique_id({_Pid, DynVar})->
+ case ts_dynvars:lookup(tsung_userid,DynVar) of
+ {ok, Val} -> ts_utils:term_to_list(Val);
+ false ->
+ ?LOG("tsung_userid not found ! Can't create unique id~n", ?ERR),
+ "0"
+ end.
+
+
%% return a really unique id, one that is unique across runs.
-get_really_unique_id({Pid, DynData}) ->
+get_really_unique_id({Pid, DynVars}) ->
Sec = ts_utils:now_sec(),
?DebugF("Sec=~p",[Sec]),
- [[integer_to_list(Sec),"-",get_unique_id({Pid, DynData})]].
+ [[integer_to_list(Sec),"-",get_unique_id({Pid, DynVars})]].
%% get an idle id (offline), and add it to the connected table
get_idle()->
@@ -304,24 +297,6 @@ fill_offline(N, Tab) when is_integer(N) ->
fill_offline(N-1, Tab).
%%%----------------------------------------------------------------------
-%%% Func: list_to_id/2
-%%% Purpose: get integer value of it's list representation in given base
-%%%----------------------------------------------------------------------
-list_to_id(L, Base) ->
- list_to_id(lists:reverse(L), 0, 1, Base).
-
-list_to_id([], Sum, _, _) -> {ok, Sum};
-list_to_id([Id|Tail], Sum, Mult, Base) when is_list(Id)->
- list_to_id([list_to_integer(Id)|Tail], Sum, Mult, Base);
-list_to_id([Id|Tail], Sum, Mult, Base) when is_integer(Id),Id < Base->
- list_to_id(Tail, Sum+Mult*Id, Mult*Base, Base);
-
-list_to_id([Id|_], _, _, Base) when is_integer(Id),Id >= Base ->
- {error, integertoohigh};
-list_to_id(Val,_,_,_) ->
- {error, {badinput, Val}}.
-
-%%%----------------------------------------------------------------------
%%% Func: ets_iterator_del/3
%%% Args: Ets, Key, Iterator
%%% Purpose: delete entry Key from Ets, update iterator if needed
3  src/tsung_controller/tsung_controller.app.src.in
View
@@ -44,7 +44,8 @@
{nclients_deb, 1}, %% beginning of interval
{nclients_fin, 2000}, %% end of interval
{config_file, "./tsung.xml"},
- {log_file, "./tsung.log"}
+ {log_file, "./tsung.log"},
+ {match_log_file, "./match.log"}
]},
{applications, [ @ERLANG_APPLICATIONS@ ]},
{start_phases, [{load_config, []},{start_os_monitoring,[{timeout,30000}]},
2  tsung-1.0.dtd
View
@@ -98,7 +98,7 @@ repeat | if )*>
<!ELEMENT match (#PCDATA)>
<!ATTLIST match
- do (continue|loop|abort|restart) "continue"
+ do (continue|loop|abort|restart|log) "continue"
when (match|nomatch) "match"
loop_back NMTOKEN "0"
max_loop NMTOKEN "20"
Please sign in to comment.
Something went wrong with that request. Please try again.