From 80a78effcae7d20d86543081ae678bfe3316961f Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 10:23:46 +0100 Subject: BUGFIX stop([fetch]) crashes when the option {file,{local,F}} has been given --- lib/observer/src/ttb.erl | 50 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 221b71df6a..7e2f835a08 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -459,13 +459,14 @@ stop() -> stop([]). stop(Opts) -> Fetch = stop_opts(Opts), - case whereis(?MODULE) of - undefined -> ok; - Pid when is_pid(Pid) -> - ?MODULE ! {stop,Fetch,self()}, - receive {?MODULE,stopped} -> ok end - end, - stopped. + Result = + case whereis(?MODULE) of + undefined -> ok; + Pid when is_pid(Pid) -> + ?MODULE ! {stop,Fetch,self()}, + receive {?MODULE,R} -> R end + end, + stop_return(Result,Opts). stop_opts(Opts) -> case lists:member(format,Opts) of @@ -478,6 +479,17 @@ stop_opts(Opts) -> end end. +stop_return(R,Opts) -> + case {lists:member(return,Opts),R} of + {true,_} -> + R; + {false,{stopped,_}} -> + stopped; + {false,_} -> + %% Anything other than 'stopped' would not be bw compatible... + stopped + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Process implementation @@ -564,12 +576,13 @@ loop(NodeInfo) -> AllNodesAndMeta), ets:delete(?history_table), wait_for_fetch(AllNodes), - io:format("Stored logs in ~s~n",[filename:absname(Dir)]), + Absname = filename:absname(Dir), + io:format("Stored logs in ~s~n",[Absname]), case FetchOrFormat of format -> format(Dir); fetch -> ok end, - Sender ! {?MODULE,stopped} + Sender ! {?MODULE,{stopped,Absname}} ?get_status end. @@ -583,18 +596,17 @@ ts() -> [Y,M,D,H,Min,S]). - fetch(Localhost,Dir,Node,MetaFile) -> - case host(Node) of - Localhost -> % same host, just move the files - Files = rpc:call(Node,observer_backend,ttb_get_filenames,[MetaFile]), + case (host(Node) == Localhost) orelse is_local(MetaFile) of + true -> % same host, just move the files + Files = get_filenames(Node,MetaFile), lists:foreach( fun(File0) -> File = filename:join(Dir,filename:basename(File0)), file:rename(File0,File) end, Files); - _Otherhost -> + false -> {ok, LSock} = gen_tcp:listen(0, [binary,{packet,2},{active,false}]), {ok,Port} = inet:port(LSock), rpc:cast(Node,observer_backend,ttb_fetch, @@ -606,6 +618,16 @@ fetch(Localhost,Dir,Node,MetaFile) -> end, ?MODULE ! {fetch_complete,Node}. +is_local({local, _, _}) -> + true; +is_local(_) -> + false. + +get_filenames(_N, {local,F,_}) -> + observer_backend:ttb_get_filenames(F); +get_filenames(N, F) -> + rpc:call(N, observer_backend,ttb_get_filenames,[F]). + receive_files(Dir,Sock,Fd) -> case gen_tcp:recv(Sock, 0) of {ok, <<0,Bin/binary>>} -> -- cgit v1.2.3 From 7124b6dcc53e521619adfe9c8bc9e0738591c839 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 11:08:54 +0100 Subject: ttb:stop([return]) should imply fetching the data --- lib/observer/src/ttb.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 7e2f835a08..f52b4f33fa 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -469,10 +469,12 @@ stop(Opts) -> stop_return(Result,Opts). stop_opts(Opts) -> - case lists:member(format,Opts) of - true -> + case {lists:member(format,Opts), lists:member(return, Opts)} of + {true, _} -> format; % format implies fetch - false -> + {_, true} -> + fetch; % if we specify return, the data should be fetched + _ -> case lists:member(fetch,Opts) of true -> fetch; false -> nofetch @@ -881,7 +883,7 @@ init_collector(Fd,Clients) -> Collected = get_first(Clients), collector(Fd,sort(Collected)). -collector(Fd,[{_,{Client,{Trace,State}}}|Rest]) -> +collector(Fd,[{_,{Client,{Trace,State}}} |Rest]) -> Trace1 = update_procinfo(Trace), State1 = handler1(Trace1,{Fd,State}), case get_next(Client,State1) of @@ -917,7 +919,7 @@ update_procinfo(Trace) -> ProcInfo = get_procinfo(Pid), setelement(2,Trace,ProcInfo). -get_procinfo(Pid) when is_pid(Pid) -> +get_procinfo(Pid) when is_pid(Pid); is_port(Pid) -> case ets:lookup(?MODULE,Pid) of [PI] -> PI; [] -> Pid -- cgit v1.2.3 From 23981b2336e52f4989ee3222e78b2baf4fb7485b Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 11:40:54 +0100 Subject: fetch is default when stopping --- lib/observer/src/ttb.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index f52b4f33fa..17520c7c84 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -471,13 +471,13 @@ stop(Opts) -> stop_opts(Opts) -> case {lists:member(format,Opts), lists:member(return, Opts)} of {true, _} -> - format; % format implies fetch + format; {_, true} -> - fetch; % if we specify return, the data should be fetched + fetch; _ -> - case lists:member(fetch,Opts) of - true -> fetch; - false -> nofetch + case lists:member(nofetch,Opts) of + true -> nofetch; + false -> fetch end end. -- cgit v1.2.3 From 9810a1c6210c9c1319a15a5cc1d16713c9e18ab8 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 12:12:02 +0100 Subject: Match specification shortcuts --- lib/observer/src/ttb.erl | 71 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 17520c7c84..4764098e29 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -365,24 +365,24 @@ proc({global,Name}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Trace pattern tp(A,B) -> - store(tp,[A,B]), - dbg:tp(A,B). + store(tp,[A,ms(B)]), + dbg:tp(A,ms(B)). tp(A,B,C) -> - store(tp,[A,B,C]), - dbg:tp(A,B,C). + store(tp,[A,B,ms(C)]), + dbg:tp(A,B,ms(C)). tp(A,B,C,D) -> - store(tp,[A,B,C,D]), - dbg:tp(A,B,C,D). + store(tp,[A,B,C,ms(D)]), + dbg:tp(A,B,C,ms(D)). tpl(A,B) -> - store(tpl,[A,B]), - dbg:tpl(A,B). + store(tpl,[A,ms(B)]), + dbg:tpl(A,ms(B)). tpl(A,B,C) -> - store(tpl,[A,B,C]), - dbg:tpl(A,B,C). + store(tpl,[A,B,ms(C)]), + dbg:tpl(A,B,ms(C)). tpl(A,B,C,D) -> - store(tpl,[A,B,C,D]), - dbg:tpl(A,B,C,D). + store(tpl,[A,B,C,ms(D)]), + dbg:tpl(A,B,C,ms(D)). ctp() -> store(ctp,[]), @@ -423,6 +423,43 @@ ctpg(A,B,C) -> store(ctpg,[A,B,C]), dbg:ctpg(A,B,C). +ms(return) -> + [{'_',[],[{return_trace}]}]; +ms(caller) -> + [{'_',[],[{message,{caller}}]}]; +ms({codestr, FunStr}) -> + {ok, MS} = string2ms(FunStr), + MS; +ms(Other) -> + Other. + +-spec string2ms(string()) -> {ok, list()} | {error, fun_format}. +string2ms(FunStr) -> + case erl_scan:string(fix_dot(FunStr)) of + {ok, Tokens, _} -> + case erl_parse:parse_exprs(Tokens) of + {ok, [Expression]} -> + case Expression of + {_, _, {clauses, Clauses}} -> + {ok, ms_transform:transform_from_shell(dbg, Clauses, [])}; + _ -> + {error, fun_format} + end; + _ -> + {error, fun_format} + end; + _ ->{error, fun_format} + end. + +-spec fix_dot(string()) -> string(). +fix_dot(FunStr) -> + [H | Rest] = lists:reverse(FunStr), + case H of + $. -> + FunStr; + H -> + lists:reverse([$., H | Rest]) + end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Support for sequential trace @@ -471,13 +508,13 @@ stop(Opts) -> stop_opts(Opts) -> case {lists:member(format,Opts), lists:member(return, Opts)} of {true, _} -> - format; + format; % format implies fetch {_, true} -> - fetch; + fetch; % if we specify return, the data should be fetched _ -> - case lists:member(nofetch,Opts) of - true -> nofetch; - false -> fetch + case lists:member(fetch,Opts) of + true -> fetch; + false -> nofetch end end. -- cgit v1.2.3 From 7611776d25dc13d8df13660e21c5b3c093e2b8c6 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 12:46:42 +0100 Subject: Allow single option when list is required --- lib/observer/src/ttb.erl | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 4764098e29..45cca218c4 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -98,8 +98,10 @@ do_tracer(Clients,PI,Traci) -> {ok,Succ} end. +opt(Opt) when is_list(Opt) -> + opt(Opt,{true,?MODULE,[]}); opt(Opt) -> - opt(Opt,{true,?MODULE,[]}). + opt([Opt]). opt([{process_info,PI}|O],{_,Client,Traci}) -> opt(O,{PI,Client,Traci}); @@ -494,7 +496,7 @@ no_store_write_trace_info(Key,What) -> %%% Stop tracing on all nodes stop() -> stop([]). -stop(Opts) -> +stop(Opts) when is_list(Opts) -> Fetch = stop_opts(Opts), Result = case whereis(?MODULE) of @@ -503,7 +505,9 @@ stop(Opts) -> ?MODULE ! {stop,Fetch,self()}, receive {?MODULE,R} -> R end end, - stop_return(Result,Opts). + stop_return(Result,Opts); +stop(Opts) -> + stop([Opts]). stop_opts(Opts) -> case {lists:member(format,Opts), lists:member(return, Opts)} of @@ -778,7 +782,7 @@ prepare(File,Handler) -> Handler1 = get_handler(Handler,Traci), {FileOrWrap,Traci,Handler1}. -format_opt(Opt) -> +format_opt(Opt) when is_list(Opt) -> Out = case lists:keysearch(out,1,Opt) of {value,{out,O}} -> O; _ -> standard_io @@ -787,7 +791,9 @@ format_opt(Opt) -> {value,{handler,H}} -> H; _ -> undefined end, - {Out,Handler}. + {Out,Handler}; +format_opt(Opt) -> + format_opt([Opt]). read_traci(File) -> -- cgit v1.2.3 From 034b105741df4e6d740ad1ec4b97df569eb2a6ca Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 12:59:56 +0100 Subject: Allow to specify fetch directory --- lib/observer/src/ttb.erl | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 45cca218c4..1a34e5b2fc 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -510,18 +510,29 @@ stop(Opts) -> stop([Opts]). stop_opts(Opts) -> + FetchDir = proplists:get_value(fetch_dir, Opts), + ensure_fetch_dir(FetchDir), case {lists:member(format,Opts), lists:member(return, Opts)} of {true, _} -> - format; % format implies fetch + {format, FetchDir}; % format implies fetch {_, true} -> - fetch; % if we specify return, the data should be fetched + {fetch, FetchDir}; % if we specify return, the data should be fetched _ -> case lists:member(fetch,Opts) of - true -> fetch; + true -> {fetch, FetchDir}; false -> nofetch end end. +ensure_fetch_dir(undefined) -> ok; +ensure_fetch_dir(Dir) -> + case filelib:is_file(Dir) of + true -> + throw({error, exists, Dir}); + false -> + ok + end. + stop_return(R,Opts) -> case {lists:member(return,Opts),R} of {true,_} -> @@ -592,9 +603,9 @@ loop(NodeInfo) -> dbg:stop_clear(), ets:delete(?history_table), Sender ! {?MODULE,stopped}; - {stop,FetchOrFormat,Sender} -> + {stop,{FetchOrFormat, UserDir} ,Sender} -> Localhost = host(node()), - Dir = ?upload_dir++ts(), + Dir = get_fetch_dir(UserDir), file:make_dir(Dir), %% The nodes are traversed twice here because %% the meta tracing in observer_backend must be @@ -629,6 +640,9 @@ loop(NodeInfo) -> ?get_status end. +get_fetch_dir(undefined) -> ?upload_dir ++ ts(); +get_fetch_dir(Dir) -> Dir. + get_nodes() -> ?MODULE ! {get_nodes,self()}, receive {?MODULE,Nodes} -> Nodes end. -- cgit v1.2.3 From f5436f5c0c44e75777a7501944a166408a0702cd Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:01:34 +0100 Subject: One-command trace setup --- lib/observer/src/ttb.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 1a34e5b2fc..2498be150d 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -20,7 +20,7 @@ -author('siri@erix.ericsson.se'). %% API --export([tracer/0,tracer/1,tracer/2,p/2,stop/0,stop/1]). +-export([tracer/0,tracer/1,tracer/2,p/2,stop/0,stop/1,start_trace/4]). -export([tp/2, tp/3, tp/4, ctp/0, ctp/1, ctp/2, ctp/3, tpl/2, tpl/3, tpl/4, ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3]). -export([seq_trigger_ms/0,seq_trigger_ms/1]). @@ -43,6 +43,15 @@ -define(get_status,). -endif. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Shortcut +start_trace(Nodes, Patterns, {Procs, Flags}, Options) -> + {ok, _} = tracer(Nodes, Options), + {ok, _} = p(Procs, Flags), + [{ok, _} = apply(?MODULE, tpl, tuple_to_list(Args)) || Args <- Patterns]. + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Open a trace port on all given nodes and create the meta data file tracer() -> tracer(node()). -- cgit v1.2.3 From 4eaa903c25d5fceb473013f4afb207a3fd82ab2e Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:14:34 +0100 Subject: BUGFIX Fetch fails when nodes on the same host have different cwd --- lib/observer/src/ttb.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 2498be150d..66b8d049d1 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -668,8 +668,11 @@ fetch(Localhost,Dir,Node,MetaFile) -> Files = get_filenames(Node,MetaFile), lists:foreach( fun(File0) -> - File = filename:join(Dir,filename:basename(File0)), - file:rename(File0,File) + %%Other nodes may still have different CWD + {ok, Cwd} = rpc:call(Node, file, get_cwd, []), + File1 = filename:join(Cwd, File0), + File = filename:join(Dir,filename:basename(File1)), + file:rename(File1,File) end, Files); false -> -- cgit v1.2.3 From 6af331934dafdfe95d30f7f7502161a3da34fd23 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:15:54 +0100 Subject: Time-constrained tracing --- lib/observer/src/ttb.erl | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 66b8d049d1..d4a90d7e42 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -37,8 +37,9 @@ -define(history_table,ttb_history_table). -define(seq_trace_flags,[send,'receive',print,timestamp]). -define(upload_dir,"ttb_upload"). +-define(last_config, "ttb_last_config"). -ifdef(debug). --define(get_status,;get_status -> erlang:display(dict:to_list(NodeInfo)),loop(NodeInfo)). +-define(get_status,;get_status -> erlang:display(dict:to_list(NodeInfo),loop(NodeInfo, TraceInfo)). -else. -define(get_status,). -endif. @@ -57,9 +58,10 @@ start_trace(Nodes, Patterns, {Procs, Flags}, Options) -> tracer() -> tracer(node()). tracer(Nodes) -> tracer(Nodes,[]). tracer(Nodes,Opt) -> - start(), - store(tracer,[Nodes,Opt]), {PI,Client,Traci} = opt(Opt), + %%We use initial Traci as SessionInfo for loop/2 + start(Traci), + store(tracer,[Nodes,Opt]), do_tracer(Nodes,PI,Client,Traci). do_tracer(Nodes0,PI,Client,Traci) -> @@ -118,6 +120,10 @@ opt([{file,Client}|O],{PI,_,Traci}) -> opt(O,{PI,Client,Traci}); opt([{handler,Handler}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{handler,Handler}|Traci]}); +opt([{timer, {MSec, StopOpts}}|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{timer,{MSec, StopOpts}}|Traci]}); +opt([{timer, MSec}|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{timer,{MSec, []}}|Traci]}); opt([],Opt) -> Opt. @@ -338,11 +344,12 @@ no_store_p(Procs0,Flags0) -> {error,Reason} -> display_warning(P,Reason), {PMatched,Ps} - end + end end,{[],[]},Procs) of {[],[]} -> {error, no_match}; {SuccMatched,Succ} -> no_store_write_trace_info(flags,{Succ,Flags}), + ?MODULE ! trace_started, {ok,SuccMatched} end end. @@ -556,24 +563,24 @@ stop_return(R,Opts) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Process implementation -start() -> +start(SessionInfo) -> case whereis(?MODULE) of undefined -> Parent = self(), - Pid = spawn(fun() -> init(Parent) end), + Pid = spawn(fun() -> init(Parent, SessionInfo) end), receive {started,Pid} -> ok end; Pid when is_pid(Pid) -> ok end. -init(Parent) -> +init(Parent, SessionInfo) -> register(?MODULE,self()), ets:new(?history_table,[ordered_set,named_table,public]), Parent ! {started,self()}, - loop(dict:new()). + loop(dict:new(), SessionInfo). -loop(NodeInfo) -> +loop(NodeInfo, SessionInfo) -> receive {init_node,Node,MetaFile,PI,Traci} -> erlang:monitor_node(Node,true), @@ -588,10 +595,10 @@ loop(NodeInfo) -> %% We will get a nodedown message undefined end, - loop(dict:store(Node,{MetaFile,MetaPid},NodeInfo)); + loop(dict:store(Node,{MetaFile,MetaPid},NodeInfo), SessionInfo); {get_nodes,Sender} -> Sender ! {?MODULE,dict:fetch_keys(NodeInfo)}, - loop(NodeInfo); + loop(NodeInfo, SessionInfo); {write_trace_info,Key,What} -> dict:fold(fun(Node,{_MetaFile,MetaPid},_) -> rpc:call(Node,observer_backend, @@ -599,10 +606,20 @@ loop(NodeInfo) -> end, ok, NodeInfo), - loop(NodeInfo); + loop(NodeInfo, SessionInfo); {nodedown,Node} -> - loop(dict:erase(Node,NodeInfo)); + loop(dict:erase(Node,NodeInfo), SessionInfo); + {timeout, StopOpts} -> + spawn(?MODULE, stop, [StopOpts]), + loop(NodeInfo, SessionInfo); + trace_started -> + case proplists:get_value(timer, SessionInfo) of + undefined -> ok; + {MSec, StopOpts} -> erlang:send_after(MSec, self(), {timeout, StopOpts}) + end, + loop(NodeInfo, SessionInfo); {stop,nofetch,Sender} -> + write_config(?last_config, all), dict:fold( fun(Node,{_,MetaPid},_) -> rpc:call(Node,observer_backend,ttb_stop,[MetaPid]) @@ -613,9 +630,10 @@ loop(NodeInfo) -> ets:delete(?history_table), Sender ! {?MODULE,stopped}; {stop,{FetchOrFormat, UserDir} ,Sender} -> + write_config(?last_config, all), Localhost = host(node()), Dir = get_fetch_dir(UserDir), - file:make_dir(Dir), + file:make_dir(Dir), %% The nodes are traversed twice here because %% the meta tracing in observer_backend must be %% stopped before dbg is stopped, and dbg must -- cgit v1.2.3 From 84cf781d8f2f97f0950cb72d2c7a442af5a8eb94 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:29:23 +0100 Subject: Always use timestamp, optional sorting --- lib/observer/src/ttb.erl | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index d4a90d7e42..65739ae7d8 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -357,7 +357,7 @@ no_store_p(Procs0,Flags0) -> transform_flags([clear]) -> [clear]; transform_flags(Flags) -> - dbg:transform_flags(Flags). + dbg:transform_flags([timestamp | Flags]). procs(Procs) when is_list(Procs) -> @@ -774,10 +774,10 @@ write_info(Nodes,PI,Traci) -> format(Files) -> format(Files,[]). format(Files,Opt) -> - {Out,Handler} = format_opt(Opt), + {Out,Handler,DisableSort} = format_opt(Opt), ets:new(?MODULE,[named_table]), - format(Files,Out,Handler). -format(File,Out,Handler) when is_list(File), is_integer(hd(File)) -> + format(Files,Out,Handler, DisableSort). +format(File,Out,Handler,DisableSort) when is_list(File), is_integer(hd(File)) -> Files = case filelib:is_dir(File) of true -> % will merge all files in the directory @@ -793,8 +793,8 @@ format(File,Out,Handler) when is_list(File), is_integer(hd(File)) -> false -> % format one file [File] end, - format(Files,Out,Handler); -format(Files,Out,Handler) when is_list(Files), is_list(hd(Files)) -> + format(Files,Out,Handler,DisableSort); +format(Files,Out,Handler,DisableSort) when is_list(Files), is_list(hd(Files)) -> StopDbg = case whereis(dbg) of undefined -> true; _ -> false @@ -802,7 +802,7 @@ format(Files,Out,Handler) when is_list(Files), is_list(hd(Files)) -> Details = lists:foldl(fun(File,Acc) -> [prepare(File,Handler)|Acc] end, [],Files), Fd = get_fd(Out), - R = do_format(Fd,Details), + R = do_format(Fd,Details,DisableSort), file:close(Fd), ets:delete(?MODULE), case StopDbg of @@ -835,7 +835,8 @@ format_opt(Opt) when is_list(Opt) -> {value,{handler,H}} -> H; _ -> undefined end, - {Out,Handler}; + DisableSort = proplists:get_value(disable_sort, Opt, false), + {Out,Handler,DisableSort}; format_opt(Opt) -> format_opt([Opt]). @@ -927,12 +928,12 @@ get_handler(Handler,Traci) -> Handler end. -do_format(Fd,Details) -> +do_format(Fd,Details,DisableSort) -> Clients = lists:foldl(fun({FileOrWrap,Traci,Handler},Acc) -> [start_client(FileOrWrap,Traci,Handler) |Acc] end,[],Details), - init_collector(Fd,Clients). + init_collector(Fd,Clients,DisableSort). start_client(FileOrWrap,Traci,et) -> @@ -966,20 +967,26 @@ defaulthandler(Fd,Trace,_Traci,initial) -> defaulthandler(_Fd,Trace,_Traci,State) -> dbg:dhandler(Trace,State). -init_collector(Fd,Clients) -> +init_collector(Fd,Clients,DisableSort) -> Collected = get_first(Clients), - collector(Fd,sort(Collected)). + case DisableSort of + true -> collector(Fd,Collected, DisableSort); + false -> collector(Fd,sort(Collected), DisableSort) + end. -collector(Fd,[{_,{Client,{Trace,State}}} |Rest]) -> +collector(Fd,[{_,{Client,{Trace,State}}} |Rest], DisableSort) -> Trace1 = update_procinfo(Trace), State1 = handler1(Trace1,{Fd,State}), case get_next(Client,State1) of end_of_trace -> handler1(end_of_trace,{Fd,State1}), - collector(Fd,Rest); - Next -> collector(Fd,sort([Next|Rest])) + collector(Fd,Rest,DisableSort); + Next -> case DisableSort of + false -> collector(Fd,sort([Next|Rest]), DisableSort); + true -> collector(Fd,[Next|Rest], DisableSort) + end end; -collector(_Fd,[]) -> +collector(_Fd,[], _) -> ok. update_procinfo({drop,_N}=Trace) -> -- cgit v1.2.3 From dbeee458ea0330a5975d650f6b60685eeb0a7665 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:31:39 +0100 Subject: Use shared handler state for all clients --- lib/observer/src/ttb.erl | 56 +++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 27 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 65739ae7d8..50df4b4be6 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -802,7 +802,7 @@ format(Files,Out,Handler,DisableSort) when is_list(Files), is_list(hd(Files)) -> Details = lists:foldl(fun(File,Acc) -> [prepare(File,Handler)|Acc] end, [],Files), Fd = get_fd(Out), - R = do_format(Fd,Details,DisableSort), + R = do_format(Fd,Details,DisableSort,Handler), file:close(Fd), ets:delete(?MODULE), case StopDbg of @@ -832,8 +832,8 @@ format_opt(Opt) when is_list(Opt) -> _ -> standard_io end, Handler = case lists:keysearch(handler,1,Opt) of - {value,{handler,H}} -> H; - _ -> undefined + {value,{handler,H}} -> H; + _ -> {fun defaulthandler/4, initial} end, DisableSort = proplists:get_value(disable_sort, Opt, false), {Out,Handler,DisableSort}; @@ -922,32 +922,26 @@ get_handler(Handler,Traci) -> undefined -> case dict:find(handler,Traci) of {ok,[H]} -> H; - error -> undefined + error -> {fun defaulthandler/4, initial} end; _ -> Handler end. -do_format(Fd,Details,DisableSort) -> - Clients = lists:foldl(fun({FileOrWrap,Traci,Handler},Acc) -> - [start_client(FileOrWrap,Traci,Handler) - |Acc] +do_format(Fd,Details,DisableSort,Handler) -> + EmptyHandler = {fun(_,_,_,_) -> ok end, ok}, + EtHandler = {{ttb_et, handler}, initial}, + Clients = lists:foldl(fun({FileOrWrap,Traci,et},Acc) -> + [start_client(FileOrWrap,Traci,EtHandler)|Acc]; + ({FileOrWrap,Traci,_},Acc) -> + [start_client(FileOrWrap,Traci,EmptyHandler)|Acc] end,[],Details), - init_collector(Fd,Clients,DisableSort). + init_collector(Fd,Clients,DisableSort,Handler). -start_client(FileOrWrap,Traci,et) -> - dbg:trace_client(file, FileOrWrap, - {fun handler/2, - {dict:to_list(Traci),{{ttb_et,handler},initial}}}); -start_client(FileOrWrap,Traci,undefined) -> - dbg:trace_client(file, FileOrWrap, - {fun handler/2, - {dict:to_list(Traci),{fun defaulthandler/4,initial}}}); start_client(FileOrWrap,Traci,Handler) -> dbg:trace_client(file, FileOrWrap, - {fun handler/2, {dict:to_list(Traci),Handler}}). - + {fun handler/2, {dict:to_list(Traci), Handler}}). handler(Trace,State) -> %% State here is only used for the initial state. The accumulated %% State is maintained by collector!!! @@ -962,31 +956,39 @@ handler1(Trace,{Fd,{Traci,{Fun,State}}}) when is_function(Fun) -> handler1(Trace,{Fd,{Traci,{{M,F},State}}}) when is_atom(M), is_atom(F) -> {Traci,{{M,F},M:F(Fd,Trace,Traci,State)}}. +%%Used to handle common state (the same for all clients) +handler2(Trace,{Fd,Traci,{Fun,State}}) when is_function(Fun) -> + {Fun, Fun(Fd, Trace, Traci, State)}; +handler2(Trace,{Fd,Traci,{{M,F},State}}) when is_atom(M), is_atom(F) -> + {{M,F}, M:F(Fd, Trace, Traci, State)}. + defaulthandler(Fd,Trace,_Traci,initial) -> dbg:dhandler(Trace,Fd); defaulthandler(_Fd,Trace,_Traci,State) -> dbg:dhandler(Trace,State). -init_collector(Fd,Clients,DisableSort) -> +init_collector(Fd,Clients,DisableSort,Handler) -> Collected = get_first(Clients), case DisableSort of - true -> collector(Fd,Collected, DisableSort); - false -> collector(Fd,sort(Collected), DisableSort) + true -> collector(Fd,Collected, DisableSort, Handler); + false -> collector(Fd,sort(Collected), DisableSort, Handler) end. -collector(Fd,[{_,{Client,{Trace,State}}} |Rest], DisableSort) -> +collector(Fd,[{_,{Client,{Trace,State}}} |Rest], DisableSort, CommonState) -> Trace1 = update_procinfo(Trace), State1 = handler1(Trace1,{Fd,State}), + CommonState2 = handler2(Trace1, {Fd, element(1, State1), CommonState}), case get_next(Client,State1) of end_of_trace -> handler1(end_of_trace,{Fd,State1}), - collector(Fd,Rest,DisableSort); + collector(Fd,Rest,DisableSort, CommonState2); Next -> case DisableSort of - false -> collector(Fd,sort([Next|Rest]), DisableSort); - true -> collector(Fd,[Next|Rest], DisableSort) + false -> collector(Fd,sort([Next|Rest]), DisableSort, CommonState2); + true -> collector(Fd,[Next|Rest], DisableSort, CommonState2) end end; -collector(_Fd,[], _) -> +collector(Fd,[], _, CommonState) -> + handler2(end_of_trace, {Fd, end_of_trace, CommonState}), ok. update_procinfo({drop,_N}=Trace) -> -- cgit v1.2.3 From 801de20df193a95722d9d765aaac48b5bd7825c3 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:33:19 +0100 Subject: Optional handler specification allowed in stop/1 --- lib/observer/src/ttb.erl | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 50df4b4be6..f7bec2bf96 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -118,8 +118,6 @@ opt([{process_info,PI}|O],{_,Client,Traci}) -> opt(O,{PI,Client,Traci}); opt([{file,Client}|O],{PI,_,Traci}) -> opt(O,{PI,Client,Traci}); -opt([{handler,Handler}|O],{PI,Client,Traci}) -> - opt(O,{PI,Client,[{handler,Handler}|Traci]}); opt([{timer, {MSec, StopOpts}}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{timer,{MSec, StopOpts}}|Traci]}); opt([{timer, MSec}|O],{PI,Client,Traci}) -> @@ -528,16 +526,21 @@ stop(Opts) -> stop_opts(Opts) -> FetchDir = proplists:get_value(fetch_dir, Opts), ensure_fetch_dir(FetchDir), - case {lists:member(format,Opts), lists:member(return, Opts)} of - {true, _} -> - {format, FetchDir}; % format implies fetch - {_, true} -> + FormatData = case proplists:get_value(format, Opts) of + undefined -> false; + true -> {format, []}; + FOpts -> {format, FOpts} + end, + case {FormatData, lists:member(return, Opts)} of + {false, true} -> {fetch, FetchDir}; % if we specify return, the data should be fetched - _ -> + {false, false} -> case lists:member(fetch,Opts) of true -> {fetch, FetchDir}; false -> nofetch - end + end; + {FormatData, _} -> + {FormatData, FetchDir} end. ensure_fetch_dir(undefined) -> ok; @@ -660,8 +663,8 @@ loop(NodeInfo, SessionInfo) -> Absname = filename:absname(Dir), io:format("Stored logs in ~s~n",[Absname]), case FetchOrFormat of - format -> format(Dir); - fetch -> ok + fetch -> ok; + {format, Opts} -> format(Dir, Opts) end, Sender ! {?MODULE,{stopped,Absname}} ?get_status @@ -823,8 +826,7 @@ prepare(File,Handler) -> ets:insert(?MODULE,{Pid,PI,Node}) end,Proci), FileOrWrap = get_file(File,Traci), - Handler1 = get_handler(Handler,Traci), - {FileOrWrap,Traci,Handler1}. + {FileOrWrap,Traci,Handler}. format_opt(Opt) when is_list(Opt) -> Out = case lists:keysearch(out,1,Opt) of @@ -912,20 +914,8 @@ check_client(Client,File) when is_tuple(Client),element(2,Client)==wrap -> check_exists(File) -> case file:read_file_info(File) of {ok,#file_info{type=regular}} -> File; - _ -> - exit({error,no_file}) - end. - - -get_handler(Handler,Traci) -> - case Handler of - undefined -> - case dict:find(handler,Traci) of - {ok,[H]} -> H; - error -> {fun defaulthandler/4, initial} - end; _ -> - Handler + exit({error,no_file}) end. do_format(Fd,Details,DisableSort,Handler) -> -- cgit v1.2.3 From 9322dac4fe0e5713d36d667335f3c5cfd70d70e1 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:35:01 +0100 Subject: et handled in a generic way --- lib/observer/src/ttb.erl | 57 ++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 33 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index f7bec2bf96..bdea80114a 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -21,6 +21,7 @@ %% API -export([tracer/0,tracer/1,tracer/2,p/2,stop/0,stop/1,start_trace/4]). +-export([get_et_handler/0]). -export([tp/2, tp/3, tp/4, ctp/0, ctp/1, ctp/2, ctp/3, tpl/2, tpl/3, tpl/4, ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3]). -export([seq_trigger_ms/0,seq_trigger_ms/1]). @@ -576,7 +577,6 @@ start(SessionInfo) -> ok end. - init(Parent, SessionInfo) -> register(?MODULE,self()), ets:new(?history_table,[ordered_set,named_table,public]), @@ -682,7 +682,6 @@ ts() -> io_lib:format("-~4.4.0w~2.2.0w~2.2.0w-~2.2.0w~2.2.0w~2.2.0w", [Y,M,D,H,Min,S]). - fetch(Localhost,Dir,Node,MetaFile) -> case (host(Node) == Localhost) orelse is_local(MetaFile) of true -> % same host, just move the files @@ -774,6 +773,9 @@ write_info(Nodes,PI,Traci) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Format binary trace logs +get_et_handler() -> + {fun ttb_et:handler/4, initial}. + format(Files) -> format(Files,[]). format(Files,Opt) -> @@ -802,7 +804,7 @@ format(Files,Out,Handler,DisableSort) when is_list(Files), is_list(hd(Files)) -> undefined -> true; _ -> false end, - Details = lists:foldl(fun(File,Acc) -> [prepare(File,Handler)|Acc] end, + Details = lists:foldl(fun(File,Acc) -> [prepare(File)|Acc] end, [],Files), Fd = get_fd(Out), R = do_format(Fd,Details,DisableSort,Handler), @@ -814,7 +816,7 @@ format(Files,Out,Handler,DisableSort) when is_list(Files), is_list(hd(Files)) -> end, R. -prepare(File,Handler) -> +prepare(File) -> {Traci,Proci} = read_traci(File), Node = get_node(Traci), lists:foreach(fun({Pid,PI}) -> @@ -826,7 +828,7 @@ prepare(File,Handler) -> ets:insert(?MODULE,{Pid,PI,Node}) end,Proci), FileOrWrap = get_file(File,Traci), - {FileOrWrap,Traci,Handler}. + {FileOrWrap,Traci}. format_opt(Opt) when is_list(Opt) -> Out = case lists:keysearch(out,1,Opt) of @@ -919,32 +921,23 @@ check_exists(File) -> end. do_format(Fd,Details,DisableSort,Handler) -> - EmptyHandler = {fun(_,_,_,_) -> ok end, ok}, - EtHandler = {{ttb_et, handler}, initial}, - Clients = lists:foldl(fun({FileOrWrap,Traci,et},Acc) -> - [start_client(FileOrWrap,Traci,EtHandler)|Acc]; - ({FileOrWrap,Traci,_},Acc) -> - [start_client(FileOrWrap,Traci,EmptyHandler)|Acc] + Clients = lists:foldl(fun({FileOrWrap,Traci},Acc) -> + [start_client(FileOrWrap,Traci)|Acc] end,[],Details), init_collector(Fd,Clients,DisableSort,Handler). - -start_client(FileOrWrap,Traci,Handler) -> +start_client(FileOrWrap,Traci) -> dbg:trace_client(file, FileOrWrap, - {fun handler/2, {dict:to_list(Traci), Handler}}). -handler(Trace,State) -> - %% State here is only used for the initial state. The accumulated - %% State is maintained by collector!!! + {fun handler/2, dict:to_list(Traci)}). + +handler(Trace,Traci) -> + %%We return our own Traci so that it not necesarry to look it up + %%This may take time if something huge has been written to it receive - {get,Collector} -> Collector ! {self(),{Trace,State}}; + {get,Collector} -> Collector ! {self(),{Trace,Traci}}; done -> ok end, - State. - -handler1(Trace,{Fd,{Traci,{Fun,State}}}) when is_function(Fun) -> - {Traci,{Fun,Fun(Fd,Trace,Traci,State)}}; -handler1(Trace,{Fd,{Traci,{{M,F},State}}}) when is_atom(M), is_atom(F) -> - {Traci,{{M,F},M:F(Fd,Trace,Traci,State)}}. + Traci. %%Used to handle common state (the same for all clients) handler2(Trace,{Fd,Traci,{Fun,State}}) when is_function(Fun) -> @@ -964,13 +957,11 @@ init_collector(Fd,Clients,DisableSort,Handler) -> false -> collector(Fd,sort(Collected), DisableSort, Handler) end. -collector(Fd,[{_,{Client,{Trace,State}}} |Rest], DisableSort, CommonState) -> +collector(Fd,[{_,{Client,{Trace,Traci}}} |Rest], DisableSort, CommonState) -> Trace1 = update_procinfo(Trace), - State1 = handler1(Trace1,{Fd,State}), - CommonState2 = handler2(Trace1, {Fd, element(1, State1), CommonState}), - case get_next(Client,State1) of + CommonState2 = handler2(Trace1, {Fd, Traci, CommonState}), + case get_next(Client) of end_of_trace -> - handler1(end_of_trace,{Fd,State1}), collector(Fd,Rest,DisableSort, CommonState2); Next -> case DisableSort of false -> collector(Fd,sort([Next|Rest]), DisableSort, CommonState2); @@ -1026,18 +1017,18 @@ get_first([Client|Clients]) -> receive {Client,{end_of_trace,_}} -> get_first(Clients); - {Client,{Trace,_State}}=Next -> + {Client,{Trace,_}}=Next -> [{timestamp(Trace),Next}|get_first(Clients)] end; get_first([]) -> []. -get_next(Client,State) when is_pid(Client) -> +get_next(Client) when is_pid(Client) -> Client ! {get,self()}, receive {Client,{end_of_trace,_}} -> end_of_trace; - {Client,{Trace,_OldState}} -> - {timestamp(Trace),{Client,{Trace,State}}} % inserting new state!! + {Client,{Trace, Traci}} -> + {timestamp(Trace),{Client,{Trace,Traci}}} end. sort(List) -> -- cgit v1.2.3 From 45dbca7a4719eb4472e41cc83aac977d0f4b1e5c Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:36:58 +0100 Subject: BUGFIX Fetch fails when nodes on the same host have different cwd --- lib/observer/src/ttb.erl | 174 ++++++++++++++++++++++++++--------------------- 1 file changed, 95 insertions(+), 79 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index bdea80114a..71afe434bf 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -21,7 +21,6 @@ %% API -export([tracer/0,tracer/1,tracer/2,p/2,stop/0,stop/1,start_trace/4]). --export([get_et_handler/0]). -export([tp/2, tp/3, tp/4, ctp/0, ctp/1, ctp/2, ctp/3, tpl/2, tpl/3, tpl/4, ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3]). -export([seq_trigger_ms/0,seq_trigger_ms/1]). @@ -119,6 +118,8 @@ opt([{process_info,PI}|O],{_,Client,Traci}) -> opt(O,{PI,Client,Traci}); opt([{file,Client}|O],{PI,_,Traci}) -> opt(O,{PI,Client,Traci}); +opt([{handler,Handler}|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{handler,Handler}|Traci]}); opt([{timer, {MSec, StopOpts}}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{timer,{MSec, StopOpts}}|Traci]}); opt([{timer, MSec}|O],{PI,Client,Traci}) -> @@ -356,7 +357,7 @@ no_store_p(Procs0,Flags0) -> transform_flags([clear]) -> [clear]; transform_flags(Flags) -> - dbg:transform_flags([timestamp | Flags]). + dbg:transform_flags(Flags). procs(Procs) when is_list(Procs) -> @@ -527,21 +528,16 @@ stop(Opts) -> stop_opts(Opts) -> FetchDir = proplists:get_value(fetch_dir, Opts), ensure_fetch_dir(FetchDir), - FormatData = case proplists:get_value(format, Opts) of - undefined -> false; - true -> {format, []}; - FOpts -> {format, FOpts} - end, - case {FormatData, lists:member(return, Opts)} of - {false, true} -> + case {lists:member(format,Opts), lists:member(return, Opts)} of + {true, _} -> + {format, FetchDir}; % format implies fetch + {_, true} -> {fetch, FetchDir}; % if we specify return, the data should be fetched - {false, false} -> + _ -> case lists:member(fetch,Opts) of true -> {fetch, FetchDir}; false -> nofetch - end; - {FormatData, _} -> - {FormatData, FetchDir} + end end. ensure_fetch_dir(undefined) -> ok; @@ -577,6 +573,7 @@ start(SessionInfo) -> ok end. + init(Parent, SessionInfo) -> register(?MODULE,self()), ets:new(?history_table,[ordered_set,named_table,public]), @@ -663,8 +660,8 @@ loop(NodeInfo, SessionInfo) -> Absname = filename:absname(Dir), io:format("Stored logs in ~s~n",[Absname]), case FetchOrFormat of - fetch -> ok; - {format, Opts} -> format(Dir, Opts) + format -> format(Dir); + fetch -> ok end, Sender ! {?MODULE,{stopped,Absname}} ?get_status @@ -682,17 +679,24 @@ ts() -> io_lib:format("-~4.4.0w~2.2.0w~2.2.0w-~2.2.0w~2.2.0w~2.2.0w", [Y,M,D,H,Min,S]). + fetch(Localhost,Dir,Node,MetaFile) -> case (host(Node) == Localhost) orelse is_local(MetaFile) of true -> % same host, just move the files Files = get_filenames(Node,MetaFile), lists:foreach( fun(File0) -> - %%Other nodes may still have different CWD - {ok, Cwd} = rpc:call(Node, file, get_cwd, []), - File1 = filename:join(Cwd, File0), - File = filename:join(Dir,filename:basename(File1)), - file:rename(File1,File) + case MetaFile of + {local, _, _} -> + File = filename:join(Dir,filename:basename(File0)), + file:rename(File0, File); + _ -> + %%Other nodes may still have different CWD + {ok, Cwd} = rpc:call(Node, file, get_cwd, []), + File1 = filename:join(Cwd, File0), + File = filename:join(Dir,filename:basename(File1)), + file:rename(File1,File) + end end, Files); false -> @@ -773,16 +777,13 @@ write_info(Nodes,PI,Traci) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Format binary trace logs -get_et_handler() -> - {fun ttb_et:handler/4, initial}. - format(Files) -> format(Files,[]). format(Files,Opt) -> - {Out,Handler,DisableSort} = format_opt(Opt), + {Out,Handler} = format_opt(Opt), ets:new(?MODULE,[named_table]), - format(Files,Out,Handler, DisableSort). -format(File,Out,Handler,DisableSort) when is_list(File), is_integer(hd(File)) -> + format(Files,Out,Handler). +format(File,Out,Handler) when is_list(File), is_integer(hd(File)) -> Files = case filelib:is_dir(File) of true -> % will merge all files in the directory @@ -798,16 +799,16 @@ format(File,Out,Handler,DisableSort) when is_list(File), is_integer(hd(File)) -> false -> % format one file [File] end, - format(Files,Out,Handler,DisableSort); -format(Files,Out,Handler,DisableSort) when is_list(Files), is_list(hd(Files)) -> + format(Files,Out,Handler); +format(Files,Out,Handler) when is_list(Files), is_list(hd(Files)) -> StopDbg = case whereis(dbg) of undefined -> true; _ -> false end, - Details = lists:foldl(fun(File,Acc) -> [prepare(File)|Acc] end, + Details = lists:foldl(fun(File,Acc) -> [prepare(File,Handler)|Acc] end, [],Files), Fd = get_fd(Out), - R = do_format(Fd,Details,DisableSort,Handler), + R = do_format(Fd,Details), file:close(Fd), ets:delete(?MODULE), case StopDbg of @@ -816,7 +817,7 @@ format(Files,Out,Handler,DisableSort) when is_list(Files), is_list(hd(Files)) -> end, R. -prepare(File) -> +prepare(File,Handler) -> {Traci,Proci} = read_traci(File), Node = get_node(Traci), lists:foreach(fun({Pid,PI}) -> @@ -828,7 +829,8 @@ prepare(File) -> ets:insert(?MODULE,{Pid,PI,Node}) end,Proci), FileOrWrap = get_file(File,Traci), - {FileOrWrap,Traci}. + Handler1 = get_handler(Handler,Traci), + {FileOrWrap,Traci,Handler1}. format_opt(Opt) when is_list(Opt) -> Out = case lists:keysearch(out,1,Opt) of @@ -836,11 +838,10 @@ format_opt(Opt) when is_list(Opt) -> _ -> standard_io end, Handler = case lists:keysearch(handler,1,Opt) of - {value,{handler,H}} -> H; - _ -> {fun defaulthandler/4, initial} + {value,{handler,H}} -> H; + _ -> undefined end, - DisableSort = proplists:get_value(disable_sort, Opt, false), - {Out,Handler,DisableSort}; + {Out,Handler}; format_opt(Opt) -> format_opt([Opt]). @@ -920,56 +921,71 @@ check_exists(File) -> exit({error,no_file}) end. -do_format(Fd,Details,DisableSort,Handler) -> - Clients = lists:foldl(fun({FileOrWrap,Traci},Acc) -> - [start_client(FileOrWrap,Traci)|Acc] - end,[],Details), - init_collector(Fd,Clients,DisableSort,Handler). -start_client(FileOrWrap,Traci) -> - dbg:trace_client(file, FileOrWrap, - {fun handler/2, dict:to_list(Traci)}). +get_handler(Handler,Traci) -> + case Handler of + undefined -> + case dict:find(handler,Traci) of + {ok,[H]} -> H; + error -> undefined + end; + _ -> + Handler + end. -handler(Trace,Traci) -> - %%We return our own Traci so that it not necesarry to look it up - %%This may take time if something huge has been written to it - receive - {get,Collector} -> Collector ! {self(),{Trace,Traci}}; +do_format(Fd,Details) -> + Clients = lists:foldl(fun({FileOrWrap,Traci,Handler},Acc) -> + [start_client(FileOrWrap,Traci,Handler) + |Acc] + end,[],Details), + init_collector(Fd,Clients). + + +start_client(FileOrWrap,Traci,et) -> + dbg:trace_client(file, FileOrWrap, + {fun handler/2, + {dict:to_list(Traci),{{ttb_et,handler},initial}}}); +start_client(FileOrWrap,Traci,undefined) -> + dbg:trace_client(file, FileOrWrap, + {fun handler/2, + {dict:to_list(Traci),{fun defaulthandler/4,initial}}}); +start_client(FileOrWrap,Traci,Handler) -> + dbg:trace_client(file, FileOrWrap, + {fun handler/2, {dict:to_list(Traci),Handler}}). + +handler(Trace,State) -> + %% State here is only used for the initial state. The accumulated + %% State is maintained by collector!!! + receive + {get,Collector} -> Collector ! {self(),{Trace,State}}; done -> ok end, - Traci. + State. -%%Used to handle common state (the same for all clients) -handler2(Trace,{Fd,Traci,{Fun,State}}) when is_function(Fun) -> - {Fun, Fun(Fd, Trace, Traci, State)}; -handler2(Trace,{Fd,Traci,{{M,F},State}}) when is_atom(M), is_atom(F) -> - {{M,F}, M:F(Fd, Trace, Traci, State)}. +handler1(Trace,{Fd,{Traci,{Fun,State}}}) when is_function(Fun) -> + {Traci,{Fun,Fun(Fd,Trace,Traci,State)}}; +handler1(Trace,{Fd,{Traci,{{M,F},State}}}) when is_atom(M), is_atom(F) -> + {Traci,{{M,F},M:F(Fd,Trace,Traci,State)}}. defaulthandler(Fd,Trace,_Traci,initial) -> dbg:dhandler(Trace,Fd); defaulthandler(_Fd,Trace,_Traci,State) -> dbg:dhandler(Trace,State). -init_collector(Fd,Clients,DisableSort,Handler) -> +init_collector(Fd,Clients) -> Collected = get_first(Clients), - case DisableSort of - true -> collector(Fd,Collected, DisableSort, Handler); - false -> collector(Fd,sort(Collected), DisableSort, Handler) - end. + collector(Fd,sort(Collected)). -collector(Fd,[{_,{Client,{Trace,Traci}}} |Rest], DisableSort, CommonState) -> +collector(Fd,[{_,{Client,{Trace,State}}} |Rest]) -> Trace1 = update_procinfo(Trace), - CommonState2 = handler2(Trace1, {Fd, Traci, CommonState}), - case get_next(Client) of - end_of_trace -> - collector(Fd,Rest,DisableSort, CommonState2); - Next -> case DisableSort of - false -> collector(Fd,sort([Next|Rest]), DisableSort, CommonState2); - true -> collector(Fd,[Next|Rest], DisableSort, CommonState2) - end + State1 = handler1(Trace1,{Fd,State}), + case get_next(Client,State1) of + end_of_trace -> + handler1(end_of_trace,{Fd,State1}), + collector(Fd,Rest); + Next -> collector(Fd,sort([Next|Rest])) end; -collector(Fd,[], _, CommonState) -> - handler2(end_of_trace, {Fd, end_of_trace, CommonState}), +collector(_Fd,[]) -> ok. update_procinfo({drop,_N}=Trace) -> @@ -1014,21 +1030,21 @@ get_procinfo({Name,Node}) when is_atom(Name) -> get_first([Client|Clients]) -> Client ! {get,self()}, - receive - {Client,{end_of_trace,_}} -> + receive + {Client,{end_of_trace,_}} -> get_first(Clients); - {Client,{Trace,_}}=Next -> + {Client,{Trace,_State}}=Next -> [{timestamp(Trace),Next}|get_first(Clients)] end; get_first([]) -> []. -get_next(Client) when is_pid(Client) -> +get_next(Client,State) when is_pid(Client) -> Client ! {get,self()}, - receive - {Client,{end_of_trace,_}} -> + receive + {Client,{end_of_trace,_}} -> end_of_trace; - {Client,{Trace, Traci}} -> - {timestamp(Trace),{Client,{Trace,Traci}}} + {Client,{Trace,_OldState}} -> + {timestamp(Trace),{Client,{Trace,State}}} % inserting new state!! end. sort(List) -> -- cgit v1.2.3 From 97fd7481fa5f6ba04b86e0251750a6f9386ca964 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:39:13 +0100 Subject: Overload protection --- lib/observer/src/ttb.erl | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 71afe434bf..0491f3c202 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -60,9 +60,9 @@ tracer(Nodes) -> tracer(Nodes,[]). tracer(Nodes,Opt) -> {PI,Client,Traci} = opt(Opt), %%We use initial Traci as SessionInfo for loop/2 - start(Traci), + Pid = start(Traci), store(tracer,[Nodes,Opt]), - do_tracer(Nodes,PI,Client,Traci). + do_tracer(Nodes,PI,Client,[{ttb_control, Pid}|Traci]). do_tracer(Nodes0,PI,Client,Traci) -> Nodes = nods(Nodes0), @@ -124,6 +124,8 @@ opt([{timer, {MSec, StopOpts}}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{timer,{MSec, StopOpts}}|Traci]}); opt([{timer, MSec}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{timer,{MSec, []}}|Traci]}); +opt([{overload_check, {MSec,M,F}}|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{overload_check,{MSec,M,F}}|Traci]}); opt([],Opt) -> Opt. @@ -330,6 +332,7 @@ arg_list([A1|A],Acc) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Set trace flags on processes p(Procs0,Flags0) -> + ensure_no_overloaded_nodes(), store(p,[Procs0,Flags0]), no_store_p(Procs0,Flags0). no_store_p(Procs0,Flags0) -> @@ -383,22 +386,28 @@ proc({global,Name}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Trace pattern tp(A,B) -> + ensure_no_overloaded_nodes(), store(tp,[A,ms(B)]), dbg:tp(A,ms(B)). tp(A,B,C) -> + ensure_no_overloaded_nodes(), store(tp,[A,B,ms(C)]), dbg:tp(A,B,ms(C)). tp(A,B,C,D) -> + ensure_no_overloaded_nodes(), store(tp,[A,B,C,ms(D)]), dbg:tp(A,B,C,ms(D)). tpl(A,B) -> + ensure_no_overloaded_nodes(), store(tpl,[A,ms(B)]), dbg:tpl(A,ms(B)). tpl(A,B,C) -> + ensure_no_overloaded_nodes(), store(tpl,[A,B,ms(C)]), dbg:tpl(A,B,ms(C)). tpl(A,B,C,D) -> + ensure_no_overloaded_nodes(), store(tpl,[A,B,C,ms(D)]), dbg:tpl(A,B,C,ms(D)). @@ -451,6 +460,14 @@ ms({codestr, FunStr}) -> ms(Other) -> Other. +ensure_no_overloaded_nodes() -> + ttb ! {get_overloaded, self()}, + Overloaded = receive O -> O after 300 -> [] end, + case Overloaded of + [] -> ok; + Overloaded -> exit({error, overload_protection_active, Overloaded}) + end. + -spec string2ms(string()) -> {ok, list()} | {error, fun_format}. string2ms(FunStr) -> case erl_scan:string(fix_dot(FunStr)) of @@ -568,9 +585,10 @@ start(SessionInfo) -> undefined -> Parent = self(), Pid = spawn(fun() -> init(Parent, SessionInfo) end), - receive {started,Pid} -> ok end; + receive {started,Pid} -> ok end, + Pid; Pid when is_pid(Pid) -> - ok + Pid end. @@ -612,6 +630,14 @@ loop(NodeInfo, SessionInfo) -> {timeout, StopOpts} -> spawn(?MODULE, stop, [StopOpts]), loop(NodeInfo, SessionInfo); + {node_overloaded, Node} -> + io:format("Overload check activated on node: ~p.~n", [Node]), + {Overloaded, SI} = {proplists:get_value(overloaded, SessionInfo, []), + lists:keydelete(overloaded, 1, SessionInfo)}, + loop(NodeInfo, [{overloaded, [Node|Overloaded]} | SI]); + {get_overloaded, Pid} -> + Pid ! proplists:get_value(overloaded, SessionInfo, []), + loop(NodeInfo, SessionInfo); trace_started -> case proplists:get_value(timer, SessionInfo) of undefined -> ok; -- cgit v1.2.3 From e0d7dd6fa8cbd49a08d9fa90c550b13c21c2dd23 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:41:11 +0100 Subject: BUGFIX Occasional deadlock on ttb:stop --- lib/observer/src/ttb.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 0491f3c202..1bc244a0d2 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -569,6 +569,8 @@ ensure_fetch_dir(Dir) -> stop_return(R,Opts) -> case {lists:member(return,Opts),R} of {true,_} -> + %%Printout moved out of the ttb loop to avoid occasional deadlock + io:format("Stored logs in ~s~n",[element(2, R)]), R; {false,{stopped,_}} -> stopped; @@ -684,7 +686,6 @@ loop(NodeInfo, SessionInfo) -> ets:delete(?history_table), wait_for_fetch(AllNodes), Absname = filename:absname(Dir), - io:format("Stored logs in ~s~n",[Absname]), case FetchOrFormat of format -> format(Dir); fetch -> ok -- cgit v1.2.3 From b605c8a54091e38bdc307cd4dffa5ab603b078c7 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:42:14 +0100 Subject: showing trace messages on the console in real time --- lib/observer/src/ttb.erl | 77 ++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 42 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 1bc244a0d2..032aecfad2 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -51,18 +51,17 @@ start_trace(Nodes, Patterns, {Procs, Flags}, Options) -> {ok, _} = p(Procs, Flags), [{ok, _} = apply(?MODULE, tpl, tuple_to_list(Args)) || Args <- Patterns]. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Open a trace port on all given nodes and create the meta data file tracer() -> tracer(node()). +tracer(shell) -> tracer(node(), shell); tracer(Nodes) -> tracer(Nodes,[]). tracer(Nodes,Opt) -> {PI,Client,Traci} = opt(Opt), %%We use initial Traci as SessionInfo for loop/2 - Pid = start(Traci), + start(Traci), store(tracer,[Nodes,Opt]), - do_tracer(Nodes,PI,Client,[{ttb_control, Pid}|Traci]). + do_tracer(Nodes,PI,Client,Traci). do_tracer(Nodes0,PI,Client,Traci) -> Nodes = nods(Nodes0), @@ -70,6 +69,7 @@ do_tracer(Nodes0,PI,Client,Traci) -> do_tracer(Clients,PI,Traci). do_tracer(Clients,PI,Traci) -> + ShellOutput = proplists:get_value(shell, Traci, false), {ClientSucc,Succ} = lists:foldl( fun({N,{local,File},TF},{CS,S}) -> @@ -80,7 +80,7 @@ do_tracer(Clients,PI,Traci) -> {ok,T} = dbg:get_tracer(N), rpc:call(N,seq_trace,set_system_tracer,[T]), dbg:trace_client(ip,{Host,Port}, - {fun ip_to_file/2,{file,File}}), + {fun ip_to_file/2,{{file,File}, ShellOutput}}), {[{N,{local,File,Port},TF}|CS], [N|S]}; Other -> display_warning(N,{cannot_open_ip_trace_port, @@ -124,10 +124,18 @@ opt([{timer, {MSec, StopOpts}}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{timer,{MSec, StopOpts}}|Traci]}); opt([{timer, MSec}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{timer,{MSec, []}}|Traci]}); -opt([{overload_check, {MSec,M,F}}|O],{PI,Client,Traci}) -> - opt(O,{PI,Client,[{overload_check,{MSec,M,F}}|Traci]}); +opt([shell|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{shell, true}|Traci]}); opt([],Opt) -> - Opt. + ensure_opt(Opt). + +ensure_opt({PI,Client,Traci}) -> + case {proplists:get_value(shell, Traci), Client} of + {undefined, _} -> {PI, Client, Traci}; + {true, ?MODULE} -> {PI, {local, ?MODULE}, Traci}; + {true, {local, File}} -> {PI, {local, File}, Traci}; + {true, _} -> exit(local_client_required_on_shell_tracing) + end. nods(all) -> Nodes1 = remove_active([node()|nodes()]), @@ -332,7 +340,6 @@ arg_list([A1|A],Acc) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Set trace flags on processes p(Procs0,Flags0) -> - ensure_no_overloaded_nodes(), store(p,[Procs0,Flags0]), no_store_p(Procs0,Flags0). no_store_p(Procs0,Flags0) -> @@ -386,28 +393,22 @@ proc({global,Name}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Trace pattern tp(A,B) -> - ensure_no_overloaded_nodes(), store(tp,[A,ms(B)]), dbg:tp(A,ms(B)). tp(A,B,C) -> - ensure_no_overloaded_nodes(), store(tp,[A,B,ms(C)]), dbg:tp(A,B,ms(C)). tp(A,B,C,D) -> - ensure_no_overloaded_nodes(), store(tp,[A,B,C,ms(D)]), dbg:tp(A,B,C,ms(D)). tpl(A,B) -> - ensure_no_overloaded_nodes(), store(tpl,[A,ms(B)]), dbg:tpl(A,ms(B)). tpl(A,B,C) -> - ensure_no_overloaded_nodes(), store(tpl,[A,B,ms(C)]), dbg:tpl(A,B,ms(C)). tpl(A,B,C,D) -> - ensure_no_overloaded_nodes(), store(tpl,[A,B,C,ms(D)]), dbg:tpl(A,B,C,ms(D)). @@ -460,14 +461,6 @@ ms({codestr, FunStr}) -> ms(Other) -> Other. -ensure_no_overloaded_nodes() -> - ttb ! {get_overloaded, self()}, - Overloaded = receive O -> O after 300 -> [] end, - case Overloaded of - [] -> ok; - Overloaded -> exit({error, overload_protection_active, Overloaded}) - end. - -spec string2ms(string()) -> {ok, list()} | {error, fun_format}. string2ms(FunStr) -> case erl_scan:string(fix_dot(FunStr)) of @@ -569,8 +562,6 @@ ensure_fetch_dir(Dir) -> stop_return(R,Opts) -> case {lists:member(return,Opts),R} of {true,_} -> - %%Printout moved out of the ttb loop to avoid occasional deadlock - io:format("Stored logs in ~s~n",[element(2, R)]), R; {false,{stopped,_}} -> stopped; @@ -587,10 +578,9 @@ start(SessionInfo) -> undefined -> Parent = self(), Pid = spawn(fun() -> init(Parent, SessionInfo) end), - receive {started,Pid} -> ok end, - Pid; + receive {started,Pid} -> ok end; Pid when is_pid(Pid) -> - Pid + ok end. @@ -632,14 +622,6 @@ loop(NodeInfo, SessionInfo) -> {timeout, StopOpts} -> spawn(?MODULE, stop, [StopOpts]), loop(NodeInfo, SessionInfo); - {node_overloaded, Node} -> - io:format("Overload check activated on node: ~p.~n", [Node]), - {Overloaded, SI} = {proplists:get_value(overloaded, SessionInfo, []), - lists:keydelete(overloaded, 1, SessionInfo)}, - loop(NodeInfo, [{overloaded, [Node|Overloaded]} | SI]); - {get_overloaded, Pid} -> - Pid ! proplists:get_value(overloaded, SessionInfo, []), - loop(NodeInfo, SessionInfo); trace_started -> case proplists:get_value(timer, SessionInfo) of undefined -> ok; @@ -686,6 +668,7 @@ loop(NodeInfo, SessionInfo) -> ets:delete(?history_table), wait_for_fetch(AllNodes), Absname = filename:absname(Dir), + io:format("Stored logs in ~s~n",[Absname]), case FetchOrFormat of format -> format(Dir); fetch -> ok @@ -1115,19 +1098,29 @@ display_warning(Item,Warning) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Trace client which reads an IP port and puts data directly to a file. %%% This is used when tracing remote nodes with no file system. -ip_to_file(Trace,{file,File}) -> +ip_to_file(Trace,{{file,File}, ShellOutput}) -> Fun = dbg:trace_port(file,File), %File can be a filename or a wrap spec Port = Fun(), - ip_to_file(Trace,Port); -ip_to_file({metadata,MetaFile,MetaData},Port) -> + case Trace of + {metadata, _, _} -> ok; + Trace -> show_trace(Trace, ShellOutput) + end, + ip_to_file(Trace,{Port,ShellOutput}); +ip_to_file({metadata,MetaFile,MetaData},State) -> {ok,MetaFd} = file:open(MetaFile,[write,raw,append]), file:write(MetaFd,MetaData), file:close(MetaFd), - Port; -ip_to_file(Trace,Port) -> + State; +ip_to_file(Trace,{Port, ShellOutput}) -> + show_trace(Trace, ShellOutput), B = term_to_binary(Trace), erlang:port_command(Port,B), - Port. + {Port, ShellOutput}. + +show_trace(Trace, true) -> + dbg:dhandler(Trace, standard_io); +show_trace(_,_) -> + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% For debugging -- cgit v1.2.3 From b3b322fd0b4d3fbabcf28035f3a62b068f8f3296 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:46:06 +0100 Subject: Autoresume tracing --- lib/observer/src/ttb.erl | 110 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 24 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 032aecfad2..78c065d2eb 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -34,10 +34,12 @@ -include_lib("kernel/include/file.hrl"). -define(meta_time,5000). +-define(fetch_time, 10000). -define(history_table,ttb_history_table). -define(seq_trace_flags,[send,'receive',print,timestamp]). -define(upload_dir,"ttb_upload"). -define(last_config, "ttb_last_config"). +-define(partial_dir, "ttb_partial_result"). -ifdef(debug). -define(get_status,;get_status -> erlang:display(dict:to_list(NodeInfo),loop(NodeInfo, TraceInfo)). -else. @@ -126,6 +128,10 @@ opt([{timer, MSec}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{timer,{MSec, []}}|Traci]}); opt([shell|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{shell, true}|Traci]}); +opt([resume|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{resume, {true, ?fetch_time}}|Traci]}); +opt([{resume,MSec}|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{resume, {true, MSec}}|Traci]}); opt([],Opt) -> ensure_opt(Opt). @@ -232,17 +238,29 @@ run_history([H|T]) -> ok -> run_history(T); {error,not_found} -> {error,{not_found,H}} end; + +run_history(all) -> + CurrentHist = ets:tab2list(?history_table), + ets:delete_all_objects(?history_table), + [run_printed(MFA,true) || {_, MFA} <- CurrentHist]; +run_history(all_silent) -> + CurrentHist = ets:tab2list(?history_table), + ets:delete_all_objects(?history_table), + [run_printed(MFA,false) || {_, MFA} <- CurrentHist]; run_history([]) -> ok; run_history(N) -> case catch ets:lookup(?history_table,N) of [{N,{M,F,A}}] -> - print_func(M,F,A), - R = apply(M,F,A), - print_result(R); + run_printed({M,F,A},true); _ -> {error, not_found} end. + +run_printed({M,F,A},Verbose) -> + Verbose andalso print_func(M,F,A), + R = apply(M,F,A), + Verbose andalso print_result(R). write_config(ConfigFile,all) -> write_config(ConfigFile,['_']); @@ -583,12 +601,12 @@ start(SessionInfo) -> ok end. - init(Parent, SessionInfo) -> register(?MODULE,self()), ets:new(?history_table,[ordered_set,named_table,public]), Parent ! {started,self()}, - loop(dict:new(), SessionInfo). + NewSessionInfo = [{partials, 0}, {dead_nodes, []} | SessionInfo], + loop(dict:new(), NewSessionInfo). loop(NodeInfo, SessionInfo) -> receive @@ -618,7 +636,13 @@ loop(NodeInfo, SessionInfo) -> NodeInfo), loop(NodeInfo, SessionInfo); {nodedown,Node} -> - loop(dict:erase(Node,NodeInfo), SessionInfo); + NewState = make_node_dead(Node, NodeInfo, SessionInfo), + loop(dict:erase(Node,NodeInfo), NewState); + {noderesumed,Node,Reporter} -> + {MetaFile, CurrentSuffix, NewState} = make_node_alive(Node, SessionInfo), + fetch_partial_result(Node, MetaFile, CurrentSuffix), + spawn(fun() -> resume_trace(Reporter) end), + loop(NodeInfo, NewState); {timeout, StopOpts} -> spawn(?MODULE, stop, [StopOpts]), loop(NodeInfo, SessionInfo); @@ -661,12 +685,13 @@ loop(NodeInfo, SessionInfo) -> AllNodes = lists:map( fun({Node,MetaFile}) -> - spawn(fun() -> fetch(Localhost,Dir,Node,MetaFile) end), + spawn(fun() -> fetch_report(Localhost,Dir,Node,MetaFile) end), Node end, AllNodesAndMeta), ets:delete(?history_table), wait_for_fetch(AllNodes), + copy_partials(Dir, proplists:get_value(partials, SessionInfo)), Absname = filename:absname(Dir), io:format("Stored logs in ~s~n",[Absname]), case FetchOrFormat of @@ -677,9 +702,25 @@ loop(NodeInfo, SessionInfo) -> ?get_status end. +make_node_dead(Node, NodeInfo, SessionInfo) -> + {MetaFile,_} = dict:fetch(Node, NodeInfo), + NewDeadNodes = [{Node, MetaFile} | proplists:get_value(dead_nodes, SessionInfo)], + [{dead_nodes, NewDeadNodes} | lists:keydelete(dead_nodes, 1, SessionInfo)]. + +make_node_alive(Node, SessionInfo) -> + DeadNodes = proplists:get_value(dead_nodes, SessionInfo), + Partials = proplists:get_value(partials, SessionInfo), + {value, {_, MetaFile}, Dn2} = lists:keytake(Node, 1, DeadNodes), + SessionInfo2 = lists:keyreplace(dead_nodes, 1, SessionInfo, {dead_nodes, Dn2}), + {MetaFile, Partials + 1, lists:keyreplace(partials, 1, SessionInfo2, {partials, Partials + 1})}. + get_fetch_dir(undefined) -> ?upload_dir ++ ts(); get_fetch_dir(Dir) -> Dir. +resume_trace(Reporter) -> + ?MODULE:run_history(all_silent), + Reporter ! trace_resumed. + get_nodes() -> ?MODULE ! {get_nodes,self()}, receive {?MODULE,Nodes} -> Nodes end. @@ -689,6 +730,28 @@ ts() -> io_lib:format("-~4.4.0w~2.2.0w~2.2.0w-~2.2.0w~2.2.0w~2.2.0w", [Y,M,D,H,Min,S]). +copy_partials(_, 0) -> + ok; +copy_partials(Dir, Num) -> + PartialDir = ?partial_dir ++ integer_to_list(Num), + file:rename(PartialDir, filename:join(Dir,PartialDir)), + copy_partials(Dir, Num - 1). + +fetch_partial_result(Node,MetaFile,Current) -> + DirName = ?partial_dir ++ integer_to_list(Current), + case file:list_dir(DirName) of + {error, enoent} -> + ok; + {ok, Files} -> + [ file:delete(filename:join(DirName, File)) || File <- Files ], + file:del_dir(DirName) + end, + file:make_dir(DirName), + fetch(host(node()), DirName, Node, MetaFile). + +fetch_report(Localhost, Dir, Node, MetaFile) -> + fetch(Localhost,Dir,Node,MetaFile), + ?MODULE ! {fetch_complete,Node}. fetch(Localhost,Dir,Node,MetaFile) -> case (host(Node) == Localhost) orelse is_local(MetaFile) of @@ -718,8 +781,7 @@ fetch(Localhost,Dir,Node,MetaFile) -> receive_files(Dir,Sock,undefined), ok = gen_tcp:close(LSock), ok = gen_tcp:close(Sock) - end, - ?MODULE ! {fetch_complete,Node}. + end. is_local({local, _, _}) -> true; @@ -749,7 +811,6 @@ host(Node) -> [_name,Host] = string:tokens(atom_to_list(Node),"@"), Host. - wait_for_fetch([]) -> ok; wait_for_fetch(Nodes) -> @@ -797,15 +858,8 @@ format(File,Out,Handler) when is_list(File), is_integer(hd(File)) -> Files = case filelib:is_dir(File) of true -> % will merge all files in the directory - MetaFiles = filelib:wildcard(filename:join(File,"*.ti")), - lists:map(fun(M) -> - Sub = string:left(M,length(M)-3), - case filelib:is_file(Sub) of - true -> Sub; - false -> Sub++".*.wrp" - end - end, - MetaFiles); + List = filelib:wildcard(filename:join(File, ?partial_dir++"*")), + lists:append(collect_files([File | List])); false -> % format one file [File] end, @@ -827,6 +881,19 @@ format(Files,Out,Handler) when is_list(Files), is_list(hd(Files)) -> end, R. +collect_files(Dirs) -> + lists:map(fun(Dir) -> + MetaFiles = filelib:wildcard(filename:join(Dir,"*.ti")), + lists:map(fun(M) -> + Sub = string:left(M,length(M)-3), + case filelib:is_file(Sub) of + true -> Sub; + false -> Sub++".*.wrp" + end + end, + MetaFiles) + end, Dirs). + prepare(File,Handler) -> {Traci,Proci} = read_traci(File), Node = get_node(Traci), @@ -855,7 +922,6 @@ format_opt(Opt) when is_list(Opt) -> format_opt(Opt) -> format_opt([Opt]). - read_traci(File) -> MetaFile = get_metafile(File), case file:read_file(MetaFile) of @@ -931,7 +997,6 @@ check_exists(File) -> exit({error,no_file}) end. - get_handler(Handler,Traci) -> case Handler of undefined -> @@ -1060,7 +1125,6 @@ get_next(Client,State) when is_pid(Client) -> sort(List) -> lists:keysort(1,List). - timestamp(Trace) when element(1,Trace) =:= trace_ts; element(1,Trace) =:= seq_trace, tuple_size(Trace) =:= 4 -> element(tuple_size(Trace),Trace); @@ -1133,5 +1197,3 @@ dump_ti(<<>>,Acc) -> dump_ti(B,Acc) -> {Term,Rest} = get_term(B), dump_ti(Rest,[Term|Acc]). - - -- cgit v1.2.3 From cb2d32fdd68656ed05636456816e51d9654fbbff Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:47:04 +0100 Subject: Flush file trace port buffers with given frequency --- lib/observer/src/ttb.erl | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 78c065d2eb..24ee94e2d2 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -132,10 +132,17 @@ opt([resume|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{resume, {true, ?fetch_time}}|Traci]}); opt([{resume,MSec}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{resume, {true, MSec}}|Traci]}); +opt([{flush,MSec}|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{flush, MSec}|Traci]}); opt([],Opt) -> ensure_opt(Opt). ensure_opt({PI,Client,Traci}) -> + case {proplists:get_value(flush, Traci), Client} of + {undefined, _} -> ok; + {_, {local, _}} -> exit(flush_unsupported_with_ip_trace_port); + {_,_} -> ok + end, case {proplists:get_value(shell, Traci), Client} of {undefined, _} -> {PI, Client, Traci}; {true, ?MODULE} -> {PI, {local, ?MODULE}, Traci}; @@ -606,6 +613,7 @@ init(Parent, SessionInfo) -> ets:new(?history_table,[ordered_set,named_table,public]), Parent ! {started,self()}, NewSessionInfo = [{partials, 0}, {dead_nodes, []} | SessionInfo], + try_send_flush_tick(NewSessionInfo), loop(dict:new(), NewSessionInfo). loop(NodeInfo, SessionInfo) -> @@ -652,6 +660,10 @@ loop(NodeInfo, SessionInfo) -> {MSec, StopOpts} -> erlang:send_after(MSec, self(), {timeout, StopOpts}) end, loop(NodeInfo, SessionInfo); + flush_timeout -> + [ dbg:flush_trace_port(Node) || Node <- dict:fetch_keys(NodeInfo) ], + try_send_flush_tick(SessionInfo), + loop(NodeInfo, SessionInfo); {stop,nofetch,Sender} -> write_config(?last_config, all), dict:fold( @@ -668,7 +680,7 @@ loop(NodeInfo, SessionInfo) -> Localhost = host(node()), Dir = get_fetch_dir(UserDir), file:make_dir(Dir), - %% The nodes are traversed twice here because + %% The nodes are traversed twice here because %% the meta tracing in observer_backend must be %% stopped before dbg is stopped, and dbg must %% be stopped before the trace logs are moved orelse @@ -714,6 +726,14 @@ make_node_alive(Node, SessionInfo) -> SessionInfo2 = lists:keyreplace(dead_nodes, 1, SessionInfo, {dead_nodes, Dn2}), {MetaFile, Partials + 1, lists:keyreplace(partials, 1, SessionInfo2, {partials, Partials + 1})}. +try_send_flush_tick(State) -> + case proplists:get_value(flush, State) of + undefined -> + ok; + MSec -> + erlang:send_after(MSec, self(), flush_timeout) + end. + get_fetch_dir(undefined) -> ?upload_dir ++ ts(); get_fetch_dir(Dir) -> Dir. -- cgit v1.2.3 From 7fdb75eb25454ed5e72b580f2c40ec12d1ace050 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:49:49 +0100 Subject: Optional handler specification allowed in stop/1 - Allowed optional handler specification in trace/2 --- lib/observer/src/ttb.erl | 335 ++++++++++++++++------------------------------- 1 file changed, 114 insertions(+), 221 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 24ee94e2d2..d5f4b52b42 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -21,6 +21,7 @@ %% API -export([tracer/0,tracer/1,tracer/2,p/2,stop/0,stop/1,start_trace/4]). +-export([get_et_handler/0]). -export([tp/2, tp/3, tp/4, ctp/0, ctp/1, ctp/2, ctp/3, tpl/2, tpl/3, tpl/4, ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3]). -export([seq_trigger_ms/0,seq_trigger_ms/1]). @@ -34,12 +35,10 @@ -include_lib("kernel/include/file.hrl"). -define(meta_time,5000). --define(fetch_time, 10000). -define(history_table,ttb_history_table). -define(seq_trace_flags,[send,'receive',print,timestamp]). -define(upload_dir,"ttb_upload"). -define(last_config, "ttb_last_config"). --define(partial_dir, "ttb_partial_result"). -ifdef(debug). -define(get_status,;get_status -> erlang:display(dict:to_list(NodeInfo),loop(NodeInfo, TraceInfo)). -else. @@ -53,10 +52,11 @@ start_trace(Nodes, Patterns, {Procs, Flags}, Options) -> {ok, _} = p(Procs, Flags), [{ok, _} = apply(?MODULE, tpl, tuple_to_list(Args)) || Args <- Patterns]. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Open a trace port on all given nodes and create the meta data file tracer() -> tracer(node()). -tracer(shell) -> tracer(node(), shell); tracer(Nodes) -> tracer(Nodes,[]). tracer(Nodes,Opt) -> {PI,Client,Traci} = opt(Opt), @@ -71,7 +71,6 @@ do_tracer(Nodes0,PI,Client,Traci) -> do_tracer(Clients,PI,Traci). do_tracer(Clients,PI,Traci) -> - ShellOutput = proplists:get_value(shell, Traci, false), {ClientSucc,Succ} = lists:foldl( fun({N,{local,File},TF},{CS,S}) -> @@ -82,7 +81,7 @@ do_tracer(Clients,PI,Traci) -> {ok,T} = dbg:get_tracer(N), rpc:call(N,seq_trace,set_system_tracer,[T]), dbg:trace_client(ip,{Host,Port}, - {fun ip_to_file/2,{{file,File}, ShellOutput}}), + {fun ip_to_file/2,{file,File}}), {[{N,{local,File,Port},TF}|CS], [N|S]}; Other -> display_warning(N,{cannot_open_ip_trace_port, @@ -126,29 +125,8 @@ opt([{timer, {MSec, StopOpts}}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{timer,{MSec, StopOpts}}|Traci]}); opt([{timer, MSec}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{timer,{MSec, []}}|Traci]}); -opt([shell|O],{PI,Client,Traci}) -> - opt(O,{PI,Client,[{shell, true}|Traci]}); -opt([resume|O],{PI,Client,Traci}) -> - opt(O,{PI,Client,[{resume, {true, ?fetch_time}}|Traci]}); -opt([{resume,MSec}|O],{PI,Client,Traci}) -> - opt(O,{PI,Client,[{resume, {true, MSec}}|Traci]}); -opt([{flush,MSec}|O],{PI,Client,Traci}) -> - opt(O,{PI,Client,[{flush, MSec}|Traci]}); opt([],Opt) -> - ensure_opt(Opt). - -ensure_opt({PI,Client,Traci}) -> - case {proplists:get_value(flush, Traci), Client} of - {undefined, _} -> ok; - {_, {local, _}} -> exit(flush_unsupported_with_ip_trace_port); - {_,_} -> ok - end, - case {proplists:get_value(shell, Traci), Client} of - {undefined, _} -> {PI, Client, Traci}; - {true, ?MODULE} -> {PI, {local, ?MODULE}, Traci}; - {true, {local, File}} -> {PI, {local, File}, Traci}; - {true, _} -> exit(local_client_required_on_shell_tracing) - end. + Opt. nods(all) -> Nodes1 = remove_active([node()|nodes()]), @@ -245,29 +223,17 @@ run_history([H|T]) -> ok -> run_history(T); {error,not_found} -> {error,{not_found,H}} end; - -run_history(all) -> - CurrentHist = ets:tab2list(?history_table), - ets:delete_all_objects(?history_table), - [run_printed(MFA,true) || {_, MFA} <- CurrentHist]; -run_history(all_silent) -> - CurrentHist = ets:tab2list(?history_table), - ets:delete_all_objects(?history_table), - [run_printed(MFA,false) || {_, MFA} <- CurrentHist]; run_history([]) -> ok; run_history(N) -> case catch ets:lookup(?history_table,N) of [{N,{M,F,A}}] -> - run_printed({M,F,A},true); + print_func(M,F,A), + R = apply(M,F,A), + print_result(R); _ -> {error, not_found} end. - -run_printed({M,F,A},Verbose) -> - Verbose andalso print_func(M,F,A), - R = apply(M,F,A), - Verbose andalso print_result(R). write_config(ConfigFile,all) -> write_config(ConfigFile,['_']); @@ -392,7 +358,7 @@ no_store_p(Procs0,Flags0) -> transform_flags([clear]) -> [clear]; transform_flags(Flags) -> - dbg:transform_flags(Flags). + dbg:transform_flags([timestamp | Flags]). procs(Procs) when is_list(Procs) -> @@ -563,16 +529,21 @@ stop(Opts) -> stop_opts(Opts) -> FetchDir = proplists:get_value(fetch_dir, Opts), ensure_fetch_dir(FetchDir), - case {lists:member(format,Opts), lists:member(return, Opts)} of - {true, _} -> - {format, FetchDir}; % format implies fetch - {_, true} -> + FormatData = case proplists:get_value(format, Opts) of + undefined -> false; + true -> {format, []}; + FOpts -> {format, FOpts} + end, + case {FormatData, lists:member(return, Opts)} of + {false, true} -> {fetch, FetchDir}; % if we specify return, the data should be fetched - _ -> + {false, false} -> case lists:member(fetch,Opts) of true -> {fetch, FetchDir}; false -> nofetch - end + end; + {FormatData, _} -> + {FormatData, FetchDir} end. ensure_fetch_dir(undefined) -> ok; @@ -612,9 +583,7 @@ init(Parent, SessionInfo) -> register(?MODULE,self()), ets:new(?history_table,[ordered_set,named_table,public]), Parent ! {started,self()}, - NewSessionInfo = [{partials, 0}, {dead_nodes, []} | SessionInfo], - try_send_flush_tick(NewSessionInfo), - loop(dict:new(), NewSessionInfo). + loop(dict:new(), SessionInfo). loop(NodeInfo, SessionInfo) -> receive @@ -644,13 +613,7 @@ loop(NodeInfo, SessionInfo) -> NodeInfo), loop(NodeInfo, SessionInfo); {nodedown,Node} -> - NewState = make_node_dead(Node, NodeInfo, SessionInfo), - loop(dict:erase(Node,NodeInfo), NewState); - {noderesumed,Node,Reporter} -> - {MetaFile, CurrentSuffix, NewState} = make_node_alive(Node, SessionInfo), - fetch_partial_result(Node, MetaFile, CurrentSuffix), - spawn(fun() -> resume_trace(Reporter) end), - loop(NodeInfo, NewState); + loop(dict:erase(Node,NodeInfo), SessionInfo); {timeout, StopOpts} -> spawn(?MODULE, stop, [StopOpts]), loop(NodeInfo, SessionInfo); @@ -660,10 +623,6 @@ loop(NodeInfo, SessionInfo) -> {MSec, StopOpts} -> erlang:send_after(MSec, self(), {timeout, StopOpts}) end, loop(NodeInfo, SessionInfo); - flush_timeout -> - [ dbg:flush_trace_port(Node) || Node <- dict:fetch_keys(NodeInfo) ], - try_send_flush_tick(SessionInfo), - loop(NodeInfo, SessionInfo); {stop,nofetch,Sender} -> write_config(?last_config, all), dict:fold( @@ -680,7 +639,7 @@ loop(NodeInfo, SessionInfo) -> Localhost = host(node()), Dir = get_fetch_dir(UserDir), file:make_dir(Dir), - %% The nodes are traversed twice here because + %% The nodes are traversed twice here because %% the meta tracing in observer_backend must be %% stopped before dbg is stopped, and dbg must %% be stopped before the trace logs are moved orelse @@ -694,53 +653,28 @@ loop(NodeInfo, SessionInfo) -> [], NodeInfo), dbg:stop_clear(), - AllNodes = + AllNodes = lists:map( fun({Node,MetaFile}) -> - spawn(fun() -> fetch_report(Localhost,Dir,Node,MetaFile) end), + spawn(fun() -> fetch(Localhost,Dir,Node,MetaFile) end), Node end, AllNodesAndMeta), ets:delete(?history_table), wait_for_fetch(AllNodes), - copy_partials(Dir, proplists:get_value(partials, SessionInfo)), Absname = filename:absname(Dir), io:format("Stored logs in ~s~n",[Absname]), case FetchOrFormat of - format -> format(Dir); - fetch -> ok + fetch -> ok; + {format, Opts} -> format(Dir, Opts) end, Sender ! {?MODULE,{stopped,Absname}} ?get_status end. -make_node_dead(Node, NodeInfo, SessionInfo) -> - {MetaFile,_} = dict:fetch(Node, NodeInfo), - NewDeadNodes = [{Node, MetaFile} | proplists:get_value(dead_nodes, SessionInfo)], - [{dead_nodes, NewDeadNodes} | lists:keydelete(dead_nodes, 1, SessionInfo)]. - -make_node_alive(Node, SessionInfo) -> - DeadNodes = proplists:get_value(dead_nodes, SessionInfo), - Partials = proplists:get_value(partials, SessionInfo), - {value, {_, MetaFile}, Dn2} = lists:keytake(Node, 1, DeadNodes), - SessionInfo2 = lists:keyreplace(dead_nodes, 1, SessionInfo, {dead_nodes, Dn2}), - {MetaFile, Partials + 1, lists:keyreplace(partials, 1, SessionInfo2, {partials, Partials + 1})}. - -try_send_flush_tick(State) -> - case proplists:get_value(flush, State) of - undefined -> - ok; - MSec -> - erlang:send_after(MSec, self(), flush_timeout) - end. - get_fetch_dir(undefined) -> ?upload_dir ++ ts(); get_fetch_dir(Dir) -> Dir. -resume_trace(Reporter) -> - ?MODULE:run_history(all_silent), - Reporter ! trace_resumed. - get_nodes() -> ?MODULE ! {get_nodes,self()}, receive {?MODULE,Nodes} -> Nodes end. @@ -750,46 +684,17 @@ ts() -> io_lib:format("-~4.4.0w~2.2.0w~2.2.0w-~2.2.0w~2.2.0w~2.2.0w", [Y,M,D,H,Min,S]). -copy_partials(_, 0) -> - ok; -copy_partials(Dir, Num) -> - PartialDir = ?partial_dir ++ integer_to_list(Num), - file:rename(PartialDir, filename:join(Dir,PartialDir)), - copy_partials(Dir, Num - 1). - -fetch_partial_result(Node,MetaFile,Current) -> - DirName = ?partial_dir ++ integer_to_list(Current), - case file:list_dir(DirName) of - {error, enoent} -> - ok; - {ok, Files} -> - [ file:delete(filename:join(DirName, File)) || File <- Files ], - file:del_dir(DirName) - end, - file:make_dir(DirName), - fetch(host(node()), DirName, Node, MetaFile). - -fetch_report(Localhost, Dir, Node, MetaFile) -> - fetch(Localhost,Dir,Node,MetaFile), - ?MODULE ! {fetch_complete,Node}. - fetch(Localhost,Dir,Node,MetaFile) -> case (host(Node) == Localhost) orelse is_local(MetaFile) of true -> % same host, just move the files Files = get_filenames(Node,MetaFile), lists:foreach( fun(File0) -> - case MetaFile of - {local, _, _} -> - File = filename:join(Dir,filename:basename(File0)), - file:rename(File0, File); - _ -> - %%Other nodes may still have different CWD - {ok, Cwd} = rpc:call(Node, file, get_cwd, []), - File1 = filename:join(Cwd, File0), - File = filename:join(Dir,filename:basename(File1)), - file:rename(File1,File) - end + %%Other nodes may still have different CWD + {ok, Cwd} = rpc:call(Node, file, get_cwd, []), + File1 = filename:join(Cwd, File0), + File = filename:join(Dir,filename:basename(File1)), + file:rename(File1,File) end, Files); false -> @@ -801,7 +706,8 @@ fetch(Localhost,Dir,Node,MetaFile) -> receive_files(Dir,Sock,undefined), ok = gen_tcp:close(LSock), ok = gen_tcp:close(Sock) - end. + end, + ?MODULE ! {fetch_complete,Node}. is_local({local, _, _}) -> true; @@ -831,6 +737,7 @@ host(Node) -> [_name,Host] = string:tokens(atom_to_list(Node),"@"), Host. + wait_for_fetch([]) -> ok; wait_for_fetch(Nodes) -> @@ -868,31 +775,42 @@ write_info(Nodes,PI,Traci) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Format binary trace logs +get_et_handler() -> + {fun ttb_et:handler/4, initial}. + format(Files) -> format(Files,[]). format(Files,Opt) -> - {Out,Handler} = format_opt(Opt), + {Out,Handler,DisableSort} = format_opt(Opt), ets:new(?MODULE,[named_table]), - format(Files,Out,Handler). -format(File,Out,Handler) when is_list(File), is_integer(hd(File)) -> + format(Files,Out,Handler, DisableSort). +format(File,Out,Handler,DisableSort) when is_list(File), is_integer(hd(File)) -> Files = case filelib:is_dir(File) of true -> % will merge all files in the directory - List = filelib:wildcard(filename:join(File, ?partial_dir++"*")), - lists:append(collect_files([File | List])); + MetaFiles = filelib:wildcard(filename:join(File,"*.ti")), + lists:map(fun(M) -> + Sub = string:left(M,length(M)-3), + case filelib:is_file(Sub) of + true -> Sub; + false -> Sub++".*.wrp" + end + end, + MetaFiles); false -> % format one file [File] end, - format(Files,Out,Handler); -format(Files,Out,Handler) when is_list(Files), is_list(hd(Files)) -> + RealHandler = get_handler(Handler, Files), + format(Files,Out,RealHandler,DisableSort); +format(Files,Out,Handler,DisableSort) when is_list(Files), is_list(hd(Files)) -> StopDbg = case whereis(dbg) of undefined -> true; _ -> false end, - Details = lists:foldl(fun(File,Acc) -> [prepare(File,Handler)|Acc] end, + Details = lists:foldl(fun(File,Acc) -> [prepare(File)|Acc] end, [],Files), Fd = get_fd(Out), - R = do_format(Fd,Details), + R = do_format(Fd,Details,DisableSort,Handler), file:close(Fd), ets:delete(?MODULE), case StopDbg of @@ -901,20 +819,17 @@ format(Files,Out,Handler) when is_list(Files), is_list(hd(Files)) -> end, R. -collect_files(Dirs) -> - lists:map(fun(Dir) -> - MetaFiles = filelib:wildcard(filename:join(Dir,"*.ti")), - lists:map(fun(M) -> - Sub = string:left(M,length(M)-3), - case filelib:is_file(Sub) of - true -> Sub; - false -> Sub++".*.wrp" - end - end, - MetaFiles) - end, Dirs). - -prepare(File,Handler) -> +get_handler(undefined, Files) -> + %%We retrieve traci from the first available file + {Traci, _} = read_traci(hd(Files)), + case dict:find(handler, Traci) of + error -> {fun defaulthandler/4, initial}; + {ok, [Handler]} -> Handler + end; +get_handler(Handler, _) -> + Handler. + +prepare(File) -> {Traci,Proci} = read_traci(File), Node = get_node(Traci), lists:foreach(fun({Pid,PI}) -> @@ -926,8 +841,7 @@ prepare(File,Handler) -> ets:insert(?MODULE,{Pid,PI,Node}) end,Proci), FileOrWrap = get_file(File,Traci), - Handler1 = get_handler(Handler,Traci), - {FileOrWrap,Traci,Handler1}. + {FileOrWrap,Traci}. format_opt(Opt) when is_list(Opt) -> Out = case lists:keysearch(out,1,Opt) of @@ -935,13 +849,15 @@ format_opt(Opt) when is_list(Opt) -> _ -> standard_io end, Handler = case lists:keysearch(handler,1,Opt) of - {value,{handler,H}} -> H; - _ -> undefined + {value,{handler,H}} -> H; + _ -> undefined end, - {Out,Handler}; + DisableSort = proplists:get_value(disable_sort, Opt, false), + {Out,Handler,DisableSort}; format_opt(Opt) -> format_opt([Opt]). + read_traci(File) -> MetaFile = get_metafile(File), case file:read_file(MetaFile) of @@ -1017,70 +933,56 @@ check_exists(File) -> exit({error,no_file}) end. -get_handler(Handler,Traci) -> - case Handler of - undefined -> - case dict:find(handler,Traci) of - {ok,[H]} -> H; - error -> undefined - end; - _ -> - Handler - end. - -do_format(Fd,Details) -> - Clients = lists:foldl(fun({FileOrWrap,Traci,Handler},Acc) -> - [start_client(FileOrWrap,Traci,Handler) - |Acc] +do_format(Fd,Details,DisableSort,Handler) -> + Clients = lists:foldl(fun({FileOrWrap,Traci},Acc) -> + [start_client(FileOrWrap,Traci)|Acc] end,[],Details), - init_collector(Fd,Clients). - + init_collector(Fd,Clients,DisableSort,Handler). -start_client(FileOrWrap,Traci,et) -> - dbg:trace_client(file, FileOrWrap, - {fun handler/2, - {dict:to_list(Traci),{{ttb_et,handler},initial}}}); -start_client(FileOrWrap,Traci,undefined) -> - dbg:trace_client(file, FileOrWrap, - {fun handler/2, - {dict:to_list(Traci),{fun defaulthandler/4,initial}}}); -start_client(FileOrWrap,Traci,Handler) -> +start_client(FileOrWrap,Traci) -> dbg:trace_client(file, FileOrWrap, - {fun handler/2, {dict:to_list(Traci),Handler}}). + {fun handler/2, dict:to_list(Traci)}). -handler(Trace,State) -> - %% State here is only used for the initial state. The accumulated - %% State is maintained by collector!!! +handler(Trace,Traci) -> + %%We return our own Traci so that it not necesarry to look it up + %%This may take time if something huge has been written to it receive - {get,Collector} -> Collector ! {self(),{Trace,State}}; + {get,Collector} -> Collector ! {self(),{Trace,Traci}}; done -> ok end, - State. + Traci. -handler1(Trace,{Fd,{Traci,{Fun,State}}}) when is_function(Fun) -> - {Traci,{Fun,Fun(Fd,Trace,Traci,State)}}; -handler1(Trace,{Fd,{Traci,{{M,F},State}}}) when is_atom(M), is_atom(F) -> - {Traci,{{M,F},M:F(Fd,Trace,Traci,State)}}. +%%Used to handle common state (the same for all clients) +handler2(Trace,{Fd,Traci,{Fun,State}}) when is_function(Fun) -> + {Fun, Fun(Fd, Trace, Traci, State)}; +handler2(Trace,{Fd,Traci,{{M,F},State}}) when is_atom(M), is_atom(F) -> + {{M,F}, M:F(Fd, Trace, Traci, State)}. defaulthandler(Fd,Trace,_Traci,initial) -> dbg:dhandler(Trace,Fd); defaulthandler(_Fd,Trace,_Traci,State) -> dbg:dhandler(Trace,State). -init_collector(Fd,Clients) -> +init_collector(Fd,Clients,DisableSort,Handler) -> Collected = get_first(Clients), - collector(Fd,sort(Collected)). + case DisableSort of + true -> collector(Fd,Collected, DisableSort, Handler); + false -> collector(Fd,sort(Collected), DisableSort, Handler) + end. -collector(Fd,[{_,{Client,{Trace,State}}} |Rest]) -> +collector(Fd,[{_,{Client,{Trace,Traci}}} |Rest], DisableSort, CommonState) -> Trace1 = update_procinfo(Trace), - State1 = handler1(Trace1,{Fd,State}), - case get_next(Client,State1) of + CommonState2 = handler2(Trace1, {Fd, Traci, CommonState}), + case get_next(Client) of end_of_trace -> - handler1(end_of_trace,{Fd,State1}), - collector(Fd,Rest); - Next -> collector(Fd,sort([Next|Rest])) + collector(Fd,Rest,DisableSort, CommonState2); + Next -> case DisableSort of + false -> collector(Fd,sort([Next|Rest]), DisableSort, CommonState2); + true -> collector(Fd,[Next|Rest], DisableSort, CommonState2) + end end; -collector(_Fd,[]) -> +collector(Fd,[], _, CommonState) -> + handler2(end_of_trace, {Fd, end_of_trace, CommonState}), ok. update_procinfo({drop,_N}=Trace) -> @@ -1128,23 +1030,24 @@ get_first([Client|Clients]) -> receive {Client,{end_of_trace,_}} -> get_first(Clients); - {Client,{Trace,_State}}=Next -> + {Client,{Trace,_}}=Next -> [{timestamp(Trace),Next}|get_first(Clients)] end; get_first([]) -> []. -get_next(Client,State) when is_pid(Client) -> +get_next(Client) when is_pid(Client) -> Client ! {get,self()}, receive {Client,{end_of_trace,_}} -> end_of_trace; - {Client,{Trace,_OldState}} -> - {timestamp(Trace),{Client,{Trace,State}}} % inserting new state!! + {Client,{Trace, Traci}} -> + {timestamp(Trace),{Client,{Trace,Traci}}} end. sort(List) -> lists:keysort(1,List). + timestamp(Trace) when element(1,Trace) =:= trace_ts; element(1,Trace) =:= seq_trace, tuple_size(Trace) =:= 4 -> element(tuple_size(Trace),Trace); @@ -1182,29 +1085,19 @@ display_warning(Item,Warning) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Trace client which reads an IP port and puts data directly to a file. %%% This is used when tracing remote nodes with no file system. -ip_to_file(Trace,{{file,File}, ShellOutput}) -> +ip_to_file(Trace,{file,File}) -> Fun = dbg:trace_port(file,File), %File can be a filename or a wrap spec Port = Fun(), - case Trace of - {metadata, _, _} -> ok; - Trace -> show_trace(Trace, ShellOutput) - end, - ip_to_file(Trace,{Port,ShellOutput}); -ip_to_file({metadata,MetaFile,MetaData},State) -> + ip_to_file(Trace,Port); +ip_to_file({metadata,MetaFile,MetaData},Port) -> {ok,MetaFd} = file:open(MetaFile,[write,raw,append]), file:write(MetaFd,MetaData), file:close(MetaFd), - State; -ip_to_file(Trace,{Port, ShellOutput}) -> - show_trace(Trace, ShellOutput), + Port; +ip_to_file(Trace,Port) -> B = term_to_binary(Trace), erlang:port_command(Port,B), - {Port, ShellOutput}. - -show_trace(Trace, true) -> - dbg:dhandler(Trace, standard_io); -show_trace(_,_) -> - ok. + Port. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% For debugging -- cgit v1.2.3 From 110687c06301146bd1f05504c2224b5182812062 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 13:51:31 +0100 Subject: dbg-type tracing --- lib/observer/src/ttb.erl | 353 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 262 insertions(+), 91 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index d5f4b52b42..dfba0411a0 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -35,10 +35,12 @@ -include_lib("kernel/include/file.hrl"). -define(meta_time,5000). +-define(fetch_time, 10000). -define(history_table,ttb_history_table). -define(seq_trace_flags,[send,'receive',print,timestamp]). -define(upload_dir,"ttb_upload"). -define(last_config, "ttb_last_config"). +-define(partial_dir, "ttb_partial_result"). -ifdef(debug). -define(get_status,;get_status -> erlang:display(dict:to_list(NodeInfo),loop(NodeInfo, TraceInfo)). -else. @@ -49,21 +51,22 @@ %%% Shortcut start_trace(Nodes, Patterns, {Procs, Flags}, Options) -> {ok, _} = tracer(Nodes, Options), - {ok, _} = p(Procs, Flags), - [{ok, _} = apply(?MODULE, tpl, tuple_to_list(Args)) || Args <- Patterns]. - + [{ok, _} = apply(?MODULE, tpl, tuple_to_list(Args)) || Args <- Patterns], + {ok, _} = p(Procs, Flags). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Open a trace port on all given nodes and create the meta data file tracer() -> tracer(node()). +tracer(shell) -> tracer(node(), shell); +tracer(dbg) -> tracer(node(), {shell, only}); tracer(Nodes) -> tracer(Nodes,[]). tracer(Nodes,Opt) -> {PI,Client,Traci} = opt(Opt), %%We use initial Traci as SessionInfo for loop/2 - start(Traci), + Pid = start(Traci), store(tracer,[Nodes,Opt]), - do_tracer(Nodes,PI,Client,Traci). + do_tracer(Nodes,PI,Client,[{ttb_control, Pid}|Traci]). do_tracer(Nodes0,PI,Client,Traci) -> Nodes = nods(Nodes0), @@ -71,9 +74,14 @@ do_tracer(Nodes0,PI,Client,Traci) -> do_tracer(Clients,PI,Traci). do_tracer(Clients,PI,Traci) -> + ShellOutput = proplists:get_value(shell, Traci, false), {ClientSucc,Succ} = lists:foldl( fun({N,{local,File},TF},{CS,S}) -> + TF2 = case ShellOutput of + only -> none; + _ -> TF + end, [_Sname,Host] = string:tokens(atom_to_list(N),"@"), case catch dbg:tracer(N,port,dbg:trace_port(ip,0)) of {ok,N} -> @@ -81,8 +89,8 @@ do_tracer(Clients,PI,Traci) -> {ok,T} = dbg:get_tracer(N), rpc:call(N,seq_trace,set_system_tracer,[T]), dbg:trace_client(ip,{Host,Port}, - {fun ip_to_file/2,{file,File}}), - {[{N,{local,File,Port},TF}|CS], [N|S]}; + {fun ip_to_file/2,{{file,File}, ShellOutput}}), + {[{N,{local,File,Port},TF2}|CS], [N|S]}; Other -> display_warning(N,{cannot_open_ip_trace_port, Host, @@ -125,8 +133,34 @@ opt([{timer, {MSec, StopOpts}}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{timer,{MSec, StopOpts}}|Traci]}); opt([{timer, MSec}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{timer,{MSec, []}}|Traci]}); +opt([{overload_check, {MSec,M,F}}|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{overload_check,{MSec,M,F}}|Traci]}); +opt([shell|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{shell, true}|Traci]}); +opt([{shell,Type}|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{shell, Type}|Traci]}); +opt([resume|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{resume, {true, ?fetch_time}}|Traci]}); +opt([{resume,MSec}|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{resume, {true, MSec}}|Traci]}); +opt([{flush,MSec}|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{flush, MSec}|Traci]}); opt([],Opt) -> - Opt. + ensure_opt(Opt). + +ensure_opt({PI,Client,Traci}) -> + case {proplists:get_value(flush, Traci), Client} of + {undefined, _} -> ok; + {_, {local, _}} -> exit(flush_unsupported_with_ip_trace_port); + {_,_} -> ok + end, + NeedIpTracer = proplists:get_value(shell, Traci, false) /= false, + case {NeedIpTracer, Client} of + {false, _} -> {PI, Client, Traci}; + {true, ?MODULE} -> {PI, {local, ?MODULE}, Traci}; + {true, {local, File}} -> {PI, {local, File}, Traci}; + {true, _} -> exit(local_client_required_on_shell_tracing) + end. nods(all) -> Nodes1 = remove_active([node()|nodes()]), @@ -223,17 +257,29 @@ run_history([H|T]) -> ok -> run_history(T); {error,not_found} -> {error,{not_found,H}} end; + +run_history(all) -> + CurrentHist = ets:tab2list(?history_table), + ets:delete_all_objects(?history_table), + [run_printed(MFA,true) || {_, MFA} <- CurrentHist]; +run_history(all_silent) -> + CurrentHist = ets:tab2list(?history_table), + ets:delete_all_objects(?history_table), + [run_printed(MFA,false) || {_, MFA} <- CurrentHist]; run_history([]) -> ok; run_history(N) -> case catch ets:lookup(?history_table,N) of [{N,{M,F,A}}] -> - print_func(M,F,A), - R = apply(M,F,A), - print_result(R); + run_printed({M,F,A},true); _ -> {error, not_found} end. + +run_printed({M,F,A},Verbose) -> + Verbose andalso print_func(M,F,A), + R = apply(M,F,A), + Verbose andalso print_result(R). write_config(ConfigFile,all) -> write_config(ConfigFile,['_']); @@ -331,6 +377,7 @@ arg_list([A1|A],Acc) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Set trace flags on processes p(Procs0,Flags0) -> + ensure_no_overloaded_nodes(), store(p,[Procs0,Flags0]), no_store_p(Procs0,Flags0). no_store_p(Procs0,Flags0) -> @@ -384,22 +431,28 @@ proc({global,Name}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Trace pattern tp(A,B) -> + ensure_no_overloaded_nodes(), store(tp,[A,ms(B)]), dbg:tp(A,ms(B)). tp(A,B,C) -> + ensure_no_overloaded_nodes(), store(tp,[A,B,ms(C)]), dbg:tp(A,B,ms(C)). tp(A,B,C,D) -> + ensure_no_overloaded_nodes(), store(tp,[A,B,C,ms(D)]), dbg:tp(A,B,C,ms(D)). tpl(A,B) -> + ensure_no_overloaded_nodes(), store(tpl,[A,ms(B)]), dbg:tpl(A,ms(B)). tpl(A,B,C) -> + ensure_no_overloaded_nodes(), store(tpl,[A,B,ms(C)]), dbg:tpl(A,B,ms(C)). tpl(A,B,C,D) -> + ensure_no_overloaded_nodes(), store(tpl,[A,B,C,ms(D)]), dbg:tpl(A,B,C,ms(D)). @@ -452,6 +505,14 @@ ms({codestr, FunStr}) -> ms(Other) -> Other. +ensure_no_overloaded_nodes() -> + ttb ! {get_overloaded, self()}, + Overloaded = receive O -> O after 300 -> [] end, + case Overloaded of + [] -> ok; + Overloaded -> exit({error, overload_protection_active, Overloaded}) + end. + -spec string2ms(string()) -> {ok, list()} | {error, fun_format}. string2ms(FunStr) -> case erl_scan:string(fix_dot(FunStr)) of @@ -537,12 +598,12 @@ stop_opts(Opts) -> case {FormatData, lists:member(return, Opts)} of {false, true} -> {fetch, FetchDir}; % if we specify return, the data should be fetched - {false, false} -> - case lists:member(fetch,Opts) of - true -> {fetch, FetchDir}; - false -> nofetch + {false, false} -> + case lists:member(nofetch,Opts) of + false -> {fetch, FetchDir}; + true -> nofetch end; - {FormatData, _} -> + {FormatData, _} -> {FormatData, FetchDir} end. @@ -558,6 +619,8 @@ ensure_fetch_dir(Dir) -> stop_return(R,Opts) -> case {lists:member(return,Opts),R} of {true,_} -> + %%Printout moved out of the ttb loop to avoid occasional deadlock + io:format("Stored logs in ~s~n",[element(2, R)]), R; {false,{stopped,_}} -> stopped; @@ -574,16 +637,19 @@ start(SessionInfo) -> undefined -> Parent = self(), Pid = spawn(fun() -> init(Parent, SessionInfo) end), - receive {started,Pid} -> ok end; + receive {started,Pid} -> ok end, + Pid; Pid when is_pid(Pid) -> - ok + Pid end. init(Parent, SessionInfo) -> register(?MODULE,self()), ets:new(?history_table,[ordered_set,named_table,public]), Parent ! {started,self()}, - loop(dict:new(), SessionInfo). + NewSessionInfo = [{partials, 0}, {dead_nodes, []} | SessionInfo], + try_send_flush_tick(NewSessionInfo), + loop(dict:new(), NewSessionInfo). loop(NodeInfo, SessionInfo) -> receive @@ -613,68 +679,118 @@ loop(NodeInfo, SessionInfo) -> NodeInfo), loop(NodeInfo, SessionInfo); {nodedown,Node} -> - loop(dict:erase(Node,NodeInfo), SessionInfo); + NewState = make_node_dead(Node, NodeInfo, SessionInfo), + loop(dict:erase(Node,NodeInfo), NewState); + {noderesumed,Node,Reporter} -> + {MetaFile, CurrentSuffix, NewState} = make_node_alive(Node, SessionInfo), + fetch_partial_result(Node, MetaFile, CurrentSuffix), + spawn(fun() -> resume_trace(Reporter) end), + loop(NodeInfo, NewState); {timeout, StopOpts} -> spawn(?MODULE, stop, [StopOpts]), loop(NodeInfo, SessionInfo); + {node_overloaded, Node} -> + io:format("Overload check activated on node: ~p.~n", [Node]), + {Overloaded, SI} = {proplists:get_value(overloaded, SessionInfo, []), + lists:keydelete(overloaded, 1, SessionInfo)}, + loop(NodeInfo, [{overloaded, [Node|Overloaded]} | SI]); + {get_overloaded, Pid} -> + Pid ! proplists:get_value(overloaded, SessionInfo, []), + loop(NodeInfo, SessionInfo); trace_started -> case proplists:get_value(timer, SessionInfo) of undefined -> ok; {MSec, StopOpts} -> erlang:send_after(MSec, self(), {timeout, StopOpts}) end, loop(NodeInfo, SessionInfo); + flush_timeout -> + [ dbg:flush_trace_port(Node) || Node <- dict:fetch_keys(NodeInfo) ], + try_send_flush_tick(SessionInfo), + loop(NodeInfo, SessionInfo); {stop,nofetch,Sender} -> - write_config(?last_config, all), - dict:fold( - fun(Node,{_,MetaPid},_) -> - rpc:call(Node,observer_backend,ttb_stop,[MetaPid]) - end, - ok, - NodeInfo), - dbg:stop_clear(), - ets:delete(?history_table), - Sender ! {?MODULE,stopped}; - {stop,{FetchOrFormat, UserDir} ,Sender} -> - write_config(?last_config, all), - Localhost = host(node()), - Dir = get_fetch_dir(UserDir), - file:make_dir(Dir), - %% The nodes are traversed twice here because - %% the meta tracing in observer_backend must be - %% stopped before dbg is stopped, and dbg must - %% be stopped before the trace logs are moved orelse - %% windows complains. - AllNodesAndMeta = - dict:fold( - fun(Node,{MetaFile,MetaPid},Nodes) -> - rpc:call(Node,observer_backend,ttb_stop,[MetaPid]), - [{Node,MetaFile}|Nodes] - end, - [], - NodeInfo), - dbg:stop_clear(), - AllNodes = - lists:map( - fun({Node,MetaFile}) -> - spawn(fun() -> fetch(Localhost,Dir,Node,MetaFile) end), - Node - end, - AllNodesAndMeta), - ets:delete(?history_table), - wait_for_fetch(AllNodes), - Absname = filename:absname(Dir), - io:format("Stored logs in ~s~n",[Absname]), - case FetchOrFormat of - fetch -> ok; - {format, Opts} -> format(Dir, Opts) - end, - Sender ! {?MODULE,{stopped,Absname}} - ?get_status + do_stop(nofetch, Sender, NodeInfo, SessionInfo); + {stop,FetchSpec,Sender} -> + case proplists:get_value(shell, SessionInfo, false) of + only -> do_stop(nofetch, Sender, NodeInfo, SessionInfo); + _ -> do_stop(FetchSpec, Sender, NodeInfo, SessionInfo) + end + end. + +do_stop(nofetch, Sender, NodeInfo, _) -> + write_config(?last_config, all), + dict:fold( + fun(Node,{_,MetaPid},_) -> + rpc:call(Node,observer_backend,ttb_stop,[MetaPid]) + end, + ok, + NodeInfo), + dbg:stop_clear(), + ets:delete(?history_table), + Sender ! {?MODULE, stopped}; + +do_stop({FetchOrFormat, UserDir}, Sender, NodeInfo, SessionInfo) -> + write_config(?last_config, all), + Localhost = host(node()), + Dir = get_fetch_dir(UserDir), + file:make_dir(Dir), + %% The nodes are traversed twice here because + %% the meta tracing in observer_backend must be + %% stopped before dbg is stopped, and dbg must + %% be stopped before the trace logs are moved orelse + %% windows complains. + AllNodesAndMeta = + dict:fold( + fun(Node,{MetaFile,MetaPid},Nodes) -> + rpc:call(Node,observer_backend,ttb_stop,[MetaPid]), + [{Node,MetaFile}|Nodes] + end, + [], + NodeInfo), + dbg:stop_clear(), + AllNodes = + lists:map( + fun({Node,MetaFile}) -> + spawn(fun() -> fetch_report(Localhost,Dir,Node,MetaFile) end), + Node + end, + AllNodesAndMeta), + ets:delete(?history_table), + wait_for_fetch(AllNodes), + copy_partials(Dir, proplists:get_value(partials, SessionInfo)), + Absname = filename:absname(Dir), + case FetchOrFormat of + fetch -> ok; + {format, Opts} -> format(Dir, Opts) + end, + Sender ! {?MODULE,{stopped,Absname}}. + +make_node_dead(Node, NodeInfo, SessionInfo) -> + {MetaFile,_} = dict:fetch(Node, NodeInfo), + NewDeadNodes = [{Node, MetaFile} | proplists:get_value(dead_nodes, SessionInfo)], + [{dead_nodes, NewDeadNodes} | lists:keydelete(dead_nodes, 1, SessionInfo)]. + +make_node_alive(Node, SessionInfo) -> + DeadNodes = proplists:get_value(dead_nodes, SessionInfo), + Partials = proplists:get_value(partials, SessionInfo), + {value, {_, MetaFile}, Dn2} = lists:keytake(Node, 1, DeadNodes), + SessionInfo2 = lists:keyreplace(dead_nodes, 1, SessionInfo, {dead_nodes, Dn2}), + {MetaFile, Partials + 1, lists:keyreplace(partials, 1, SessionInfo2, {partials, Partials + 1})}. + +try_send_flush_tick(State) -> + case proplists:get_value(flush, State) of + undefined -> + ok; + MSec -> + erlang:send_after(MSec, self(), flush_timeout) end. get_fetch_dir(undefined) -> ?upload_dir ++ ts(); get_fetch_dir(Dir) -> Dir. +resume_trace(Reporter) -> + ?MODULE:run_history(all_silent), + Reporter ! trace_resumed. + get_nodes() -> ?MODULE ! {get_nodes,self()}, receive {?MODULE,Nodes} -> Nodes end. @@ -684,17 +800,46 @@ ts() -> io_lib:format("-~4.4.0w~2.2.0w~2.2.0w-~2.2.0w~2.2.0w~2.2.0w", [Y,M,D,H,Min,S]). +copy_partials(_, 0) -> + ok; +copy_partials(Dir, Num) -> + PartialDir = ?partial_dir ++ integer_to_list(Num), + file:rename(PartialDir, filename:join(Dir,PartialDir)), + copy_partials(Dir, Num - 1). + +fetch_partial_result(Node,MetaFile,Current) -> + DirName = ?partial_dir ++ integer_to_list(Current), + case file:list_dir(DirName) of + {error, enoent} -> + ok; + {ok, Files} -> + [ file:delete(filename:join(DirName, File)) || File <- Files ], + file:del_dir(DirName) + end, + file:make_dir(DirName), + fetch(host(node()), DirName, Node, MetaFile). + +fetch_report(Localhost, Dir, Node, MetaFile) -> + fetch(Localhost,Dir,Node,MetaFile), + ?MODULE ! {fetch_complete,Node}. + fetch(Localhost,Dir,Node,MetaFile) -> case (host(Node) == Localhost) orelse is_local(MetaFile) of true -> % same host, just move the files Files = get_filenames(Node,MetaFile), lists:foreach( fun(File0) -> - %%Other nodes may still have different CWD - {ok, Cwd} = rpc:call(Node, file, get_cwd, []), - File1 = filename:join(Cwd, File0), - File = filename:join(Dir,filename:basename(File1)), - file:rename(File1,File) + case MetaFile of + {local, _, _} -> + File = filename:join(Dir,filename:basename(File0)), + file:rename(File0, File); + _ -> + %%Other nodes may still have different CWD + {ok, Cwd} = rpc:call(Node, file, get_cwd, []), + File1 = filename:join(Cwd, File0), + File = filename:join(Dir,filename:basename(File1)), + file:rename(File1,File) + end end, Files); false -> @@ -706,8 +851,7 @@ fetch(Localhost,Dir,Node,MetaFile) -> receive_files(Dir,Sock,undefined), ok = gen_tcp:close(LSock), ok = gen_tcp:close(Sock) - end, - ?MODULE ! {fetch_complete,Node}. + end. is_local({local, _, _}) -> true; @@ -759,9 +903,14 @@ wait_for_fetch(Nodes) -> %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - write_info(Nodes,PI,Traci) -> - lists:foreach(fun({N,{local,C,_},F}) -> - MetaFile = F ++ ".ti", - file:delete(MetaFile), + lists:foreach(fun({N,{local,C,_},F}) -> + MetaFile = case F of + none -> + none; + F -> + file:delete(F ++ ".ti"), + F ++ ".ti" + end, Traci1 = [{node,N},{file,C}|Traci], {ok,Port} = dbg:get_tracer(N), ?MODULE ! @@ -788,15 +937,8 @@ format(File,Out,Handler,DisableSort) when is_list(File), is_integer(hd(File)) -> Files = case filelib:is_dir(File) of true -> % will merge all files in the directory - MetaFiles = filelib:wildcard(filename:join(File,"*.ti")), - lists:map(fun(M) -> - Sub = string:left(M,length(M)-3), - case filelib:is_file(Sub) of - true -> Sub; - false -> Sub++".*.wrp" - end - end, - MetaFiles); + List = filelib:wildcard(filename:join(File, ?partial_dir++"*")), + lists:append(collect_files([File | List])); false -> % format one file [File] end, @@ -819,6 +961,19 @@ format(Files,Out,Handler,DisableSort) when is_list(Files), is_list(hd(Files)) -> end, R. +collect_files(Dirs) -> + lists:map(fun(Dir) -> + MetaFiles = filelib:wildcard(filename:join(Dir,"*.ti")), + lists:map(fun(M) -> + Sub = string:left(M,length(M)-3), + case filelib:is_file(Sub) of + true -> Sub; + false -> Sub++".*.wrp" + end + end, + MetaFiles) + end, Dirs). + get_handler(undefined, Files) -> %%We retrieve traci from the first available file {Traci, _} = read_traci(hd(Files)), @@ -933,6 +1088,7 @@ check_exists(File) -> exit({error,no_file}) end. + do_format(Fd,Details,DisableSort,Handler) -> Clients = lists:foldl(fun({FileOrWrap,Traci},Acc) -> [start_client(FileOrWrap,Traci)|Acc] @@ -1085,19 +1241,34 @@ display_warning(Item,Warning) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Trace client which reads an IP port and puts data directly to a file. %%% This is used when tracing remote nodes with no file system. -ip_to_file(Trace,{file,File}) -> +ip_to_file({metadata,_,_},{_, only} = State) -> + State; +ip_to_file(Trace, {_, only} = State) -> + dbg:dhandler(Trace, standard_io), + State; +ip_to_file(Trace,{{file,File}, ShellOutput}) -> Fun = dbg:trace_port(file,File), %File can be a filename or a wrap spec Port = Fun(), - ip_to_file(Trace,Port); -ip_to_file({metadata,MetaFile,MetaData},Port) -> + case Trace of + {metadata, _, _} -> ok; + Trace -> show_trace(Trace, ShellOutput) + end, + ip_to_file(Trace,{Port,ShellOutput}); +ip_to_file({metadata,MetaFile,MetaData},State) -> {ok,MetaFd} = file:open(MetaFile,[write,raw,append]), file:write(MetaFd,MetaData), file:close(MetaFd), - Port; -ip_to_file(Trace,Port) -> + State; +ip_to_file(Trace,{Port, ShellOutput}) -> + show_trace(Trace, ShellOutput), B = term_to_binary(Trace), erlang:port_command(Port,B), - Port. + {Port, ShellOutput}. + +show_trace(Trace, true) -> + dbg:dhandler(Trace, standard_io); +show_trace(_,_) -> + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% For debugging -- cgit v1.2.3 From 88192facdb334d6b4fa4b9b98aebe6dbd0d09673 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 14:04:37 +0100 Subject: Added missing ttb_autostart.erl --- lib/runtime_tools/src/ttb_autostart.erl | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 lib/runtime_tools/src/ttb_autostart.erl (limited to 'lib') diff --git a/lib/runtime_tools/src/ttb_autostart.erl b/lib/runtime_tools/src/ttb_autostart.erl new file mode 100644 index 0000000000..4c6971c119 --- /dev/null +++ b/lib/runtime_tools/src/ttb_autostart.erl @@ -0,0 +1,55 @@ +%%%------------------------------------------------------------------- +%%% File : ttb_autostart.erl +%%% Author : Bartłomiej Puzoń +%%% Description : This supervisor is used to resume ttb tracing +%%% Users are able to provide custom restart modules for *_config, as +%%% file:write/read/delete may not be possible on diskless nodes. +%%% +%%% Created : 31 Jul 2010 by +%%%------------------------------------------------------------------- +-module(ttb_autostart). + +-behaviour(gen_server). + +%% API +-export([start_link/0, + read_config/0, + write_config/1, + delete_config/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-define(DEF_AUTOSTART_MODULE, ?MODULE). +-define(AUTOSTART_FILENAME, "ttb_autostart.bin"). + +start_link() -> + gen_server:start_link(?MODULE, no_args, []). + +delete_config() -> + file:delete(?AUTOSTART_FILENAME). + +read_config() -> + case file:read_file(?AUTOSTART_FILENAME) of + {ok, Data} -> {ok, binary_to_term(Data)}; + Error -> Error + end. + +write_config(Data) -> + file:write_file(?AUTOSTART_FILENAME, term_to_binary(Data)). + +init(no_args) -> + case application:get_env(runtime_tools, ttb_autostart_module) of + {ok, _} -> ok; + undefined -> application:set_env(runtime_tools, ttb_autostart_module, ?DEF_AUTOSTART_MODULE) + end, + observer_backend:ttb_resume_trace(), + %%As the process is not needed any more, it will shut itself down + {ok, no_args, 10000}. + +handle_call(_,_,_) -> {noreply, no_args}. +handle_cast(_,_) -> {noreply, no_args}. +handle_info(timeout,_) -> {stop, normal, no_args}. +terminate(_,_) -> ok. +code_change(_,_,_) -> {ok, no_args}. -- cgit v1.2.3 From 3f3ce0f69e9ec34106589af7afc4494be17fea5b Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 9 Mar 2011 14:13:48 +0100 Subject: Include logfile name in fetch directory name --- lib/observer/src/ttb.erl | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index dfba0411a0..7c19bd7597 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -38,7 +38,7 @@ -define(fetch_time, 10000). -define(history_table,ttb_history_table). -define(seq_trace_flags,[send,'receive',print,timestamp]). --define(upload_dir,"ttb_upload"). +-define(upload_dir(Logname),"ttb_upload_"++Logname). -define(last_config, "ttb_last_config"). -define(partial_dir, "ttb_partial_result"). -ifdef(debug). @@ -126,7 +126,7 @@ opt(Opt) -> opt([{process_info,PI}|O],{_,Client,Traci}) -> opt(O,{PI,Client,Traci}); opt([{file,Client}|O],{PI,_,Traci}) -> - opt(O,{PI,Client,Traci}); + opt(O,{PI,Client,[{logfile,get_logname(Client)}|Traci]}); opt([{handler,Handler}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{handler,Handler}|Traci]}); opt([{timer, {MSec, StopOpts}}|O],{PI,Client,Traci}) -> @@ -162,6 +162,9 @@ ensure_opt({PI,Client,Traci}) -> {true, _} -> exit(local_client_required_on_shell_tracing) end. +get_logname({local, F}) -> filename:basename(F); +get_logname(F) -> filename:basename(F). + nods(all) -> Nodes1 = remove_active([node()|nodes()]), remove_faulty_runtime_tools_vsn(Nodes1); @@ -731,7 +734,7 @@ do_stop(nofetch, Sender, NodeInfo, _) -> do_stop({FetchOrFormat, UserDir}, Sender, NodeInfo, SessionInfo) -> write_config(?last_config, all), Localhost = host(node()), - Dir = get_fetch_dir(UserDir), + Dir = get_fetch_dir(UserDir, proplists:get_value(logfile, SessionInfo)), file:make_dir(Dir), %% The nodes are traversed twice here because %% the meta tracing in observer_backend must be @@ -784,8 +787,9 @@ try_send_flush_tick(State) -> erlang:send_after(MSec, self(), flush_timeout) end. -get_fetch_dir(undefined) -> ?upload_dir ++ ts(); -get_fetch_dir(Dir) -> Dir. +get_fetch_dir(undefined,undefined) -> ?upload_dir(?MODULE_STRING) ++ ts(); +get_fetch_dir(undefined,Logname) -> ?upload_dir(Logname) ++ ts(); +get_fetch_dir(Dir,_) -> Dir. resume_trace(Reporter) -> ?MODULE:run_history(all_silent), -- cgit v1.2.3 From 5369c0bfc6994a21b81c8ee2e9231455b0d42c50 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 16 Mar 2011 09:34:13 +0100 Subject: (BUGFIX) Error with ip tracing to wrap sets --- lib/observer/src/ttb.erl | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 7c19bd7597..be996512af 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -18,6 +18,7 @@ %% -module(ttb). -author('siri@erix.ericsson.se'). +-author('bartlomiej.puzon@erlang-solutions.com'). %% API -export([tracer/0,tracer/1,tracer/2,p/2,stop/0,stop/1,start_trace/4]). @@ -162,7 +163,9 @@ ensure_opt({PI,Client,Traci}) -> {true, _} -> exit(local_client_required_on_shell_tracing) end. -get_logname({local, F}) -> filename:basename(F); +get_logname({local, F}) -> get_logname(F); +get_logname({wrap, F}) -> filename:basename(F); +get_logname({wrap, F, _, _}) -> filename:basename(F); get_logname(F) -> filename:basename(F). nods(all) -> @@ -290,6 +293,8 @@ write_config(ConfigFile,Config) -> write_config(ConfigFile,Config,[]). write_config(ConfigFile,all,Opt) -> write_config(ConfigFile,['_'],Opt); +write_config(ConfigFile,Config,Opt) when not(is_list(Opt)) -> + write_config(ConfigFile,Config,[Opt]); write_config(ConfigFile,Nums,Opt) when is_list(Nums), is_integer(hd(Nums)); Nums=:=['_'] -> F = fun(N) -> ets:select(?history_table, @@ -509,8 +514,13 @@ ms(Other) -> Other. ensure_no_overloaded_nodes() -> - ttb ! {get_overloaded, self()}, - Overloaded = receive O -> O after 300 -> [] end, + Overloaded = case whereis(?MODULE) of + undefined -> + []; + _ -> + ?MODULE ! {get_overloaded, self()}, + receive O -> O end + end, case Overloaded of [] -> ok; Overloaded -> exit({error, overload_protection_active, Overloaded}) @@ -946,8 +956,7 @@ format(File,Out,Handler,DisableSort) when is_list(File), is_integer(hd(File)) -> false -> % format one file [File] end, - RealHandler = get_handler(Handler, Files), - format(Files,Out,RealHandler,DisableSort); + format(Files,Out,Handler,DisableSort); format(Files,Out,Handler,DisableSort) when is_list(Files), is_list(hd(Files)) -> StopDbg = case whereis(dbg) of undefined -> true; @@ -956,7 +965,8 @@ format(Files,Out,Handler,DisableSort) when is_list(Files), is_list(hd(Files)) -> Details = lists:foldl(fun(File,Acc) -> [prepare(File)|Acc] end, [],Files), Fd = get_fd(Out), - R = do_format(Fd,Details,DisableSort,Handler), + RealHandler = get_handler(Handler, Files), + R = do_format(Fd,Details,DisableSort,RealHandler), file:close(Fd), ets:delete(?MODULE), case StopDbg of @@ -1271,8 +1281,14 @@ ip_to_file(Trace,{Port, ShellOutput}) -> show_trace(Trace, true) -> dbg:dhandler(Trace, standard_io); -show_trace(_,_) -> - ok. +show_trace(_Trace, false) -> + ok; +show_trace(Trace, Pid) when is_pid(Pid) -> + %%This is only to enable erlide to build trace views in real time. + %%Sending trace data to handlers in real time has however + %%to be considered an interesting feature and should be + %%implemented in a generic way in the future + Pid ! {trace, Trace}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% For debugging -- cgit v1.2.3 From 65a8c4feec2c09a9b980065cdc05cc848d82e18d Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 16 Mar 2011 11:14:10 +0100 Subject: updated OTP tests --- lib/observer/test/ttb_SUITE.erl | 104 +++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 54 deletions(-) (limited to 'lib') diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl index 14bd1e9c33..dbeee978b1 100644 --- a/lib/observer/test/ttb_SUITE.erl +++ b/lib/observer/test/ttb_SUITE.erl @@ -92,15 +92,15 @@ file(Config) when is_list(Config) -> ?line {ok,[{matched,_,1},{matched,_,1}]} = ttb:tp(?MODULE,foo,[]), ?line ?MODULE:foo(), ?line rpc:call(OtherNode,?MODULE,foo,[]), - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line ?t:stop_node(OtherNode), ?line ok = ttb:format(filename:join(Privdir,atom_to_list(Node)++"-file")), ?line ok = ttb:format(filename:join(Privdir, atom_to_list(OtherNode)++"-file")), - ?line [{trace,{S,_,Node},call,{?MODULE,foo,[]}}, + ?line [{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace, - {trace,{_,_,OtherNode},call,{?MODULE,foo,[]}}, + {trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace] = flush(), ok. @@ -123,15 +123,15 @@ file_no_pi(Config) when is_list(Config) -> ?line {ok,[{matched,_,1},{matched,_,1}]} = ttb:tp(?MODULE,foo,[]), ?line ?MODULE:foo(), ?line rpc:call(OtherNode,?MODULE,foo,[]), - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line ?t:stop_node(OtherNode), ?line ok = ttb:format(filename:join(Privdir,atom_to_list(Node)++"-file")), ?line ok = ttb:format(filename:join(Privdir, atom_to_list(OtherNode)++"-file")), - ?line [{trace,LocalProc,call,{?MODULE,foo,[]}}, + ?line [{trace_ts,LocalProc,call,{?MODULE,foo,[]}, {_,_,_}}, end_of_trace, - {trace,RemoteProc,call,{?MODULE,foo,[]}}, + {trace_ts,RemoteProc,call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace] = flush(), ?line true = is_pid(LocalProc), ?line true = is_pid(RemoteProc), @@ -170,7 +170,7 @@ file_fetch(Config) when is_list(Config) -> ?line ?MODULE:foo(), ?line rpc:call(OtherNode,?MODULE,foo,[]), ?line ?t:capture_start(), - ?line ttb:stop([fetch]), + ?line ttb:stop([return]), ?line ?t:capture_stop(), ?line [StoreString] = ?t:capture_get(), ?line UploadDir = @@ -194,9 +194,9 @@ file_fetch(Config) when is_list(Config) -> ?line ok = ttb:format(filename:join(UploadDir, atom_to_list(OtherNode)++"-file_fetch")), - ?line [{trace,{S,_,Node},call,{?MODULE,foo,[]}}, + ?line [{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace, - {trace,{_,_,OtherNode},call,{?MODULE,foo,[]}}, + {trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace] = flush(), ?line ok = file:set_cwd(Cwd), @@ -224,19 +224,19 @@ wrap(Config) when is_list(Config) -> ?line rpc:call(OtherNode,?MODULE,foo,[]), ?line ?MODULE:foo(), ?line rpc:call(OtherNode,?MODULE,foo,[]), - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line ?t:stop_node(OtherNode), ?line ok = ttb:format(filename:join(Privdir, atom_to_list(Node)++"-wrap.*.wrp")), - ?line [{trace,{S,_,Node},call,{?MODULE,foo,[]}}, - {trace,{S,_,Node},call,{?MODULE,foo,[]}}, - {trace,{S,_,Node},call,{?MODULE,foo,[]}}, + ?line [{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}}, + {trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}}, + {trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace] = flush(), ?line ok = ttb:format(filename:join(Privdir, atom_to_list(OtherNode)++"-wrap.*.wrp")), - ?line [{trace,{_,_,OtherNode},call,{?MODULE,foo,[]}}, - {trace,{_,_,OtherNode},call,{?MODULE,foo,[]}}, - {trace,{_,_,OtherNode},call,{?MODULE,foo,[]}}, + ?line [{trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},{_,_,_}}, + {trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},{_,_,_}}, + {trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace] = flush(), %% Check that merge does not crash even if the timestamp flag is not on. @@ -244,14 +244,13 @@ wrap(Config) when is_list(Config) -> [filename:join(Privdir, atom_to_list(Node)++"-wrap.*.wrp"), filename:join(Privdir, - atom_to_list(OtherNode)++"-wrap.*.wrp")]), - ?line [{trace,{S,_,Node},call,{?MODULE,foo,[]}}, - {trace,{S,_,Node},call,{?MODULE,foo,[]}}, - {trace,{S,_,Node},call,{?MODULE,foo,[]}}, - end_of_trace, - {trace,{_,_,OtherNode},call,{?MODULE,foo,[]}}, - {trace,{_,_,OtherNode},call,{?MODULE,foo,[]}}, - {trace,{_,_,OtherNode},call,{?MODULE,foo,[]}}, + atom_to_list(OtherNode)++"-wrap.*.wrp")],[{disable_sort,true}]), + ?line [{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}}, + {trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}}, + {trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}}, + {trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},{_,_,_}}, + {trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},{_,_,_}}, + {trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace] = flush(), ok. @@ -277,7 +276,7 @@ wrap_merge(Config) when is_list(Config) -> ?line rpc:call(OtherNode,?MODULE,foo,[]), ?line ?MODULE:foo(), ?line rpc:call(OtherNode,?MODULE,foo,[]), - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line ?t:stop_node(OtherNode), ?line ok = ttb:format( [filename:join(Privdir, @@ -289,7 +288,6 @@ wrap_merge(Config) when is_list(Config) -> {trace_ts,{S,_,Node},call,{?MODULE,foo,[]},_}, {trace_ts,_,call,{?MODULE,foo,[]},_}, {trace_ts,{S,_,Node},call,{?MODULE,foo,[]},_}, - end_of_trace, {trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},_}, end_of_trace] = flush(), ok. @@ -330,7 +328,6 @@ wrap_merge_fetch_format(Config) when is_list(Config) -> {trace_ts,{S,_,Node},call,{?MODULE,foo,[]},_}, {trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},_}, {trace_ts,{S,_,Node},call,{?MODULE,foo,[]},_}, - end_of_trace, {trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},_}, end_of_trace] = flush(), @@ -360,16 +357,15 @@ write_config1(Config) when is_list(Config) -> ?line ok = ttb:run_config(File), ?line ?MODULE:foo(), ?line rpc:call(OtherNode,?MODULE,foo,[]), - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line ?t:stop_node(OtherNode), ?line ok = ttb:format( [filename:join(Privdir, atom_to_list(Node)++"-write_config1"), filename:join(Privdir, atom_to_list(OtherNode)++"-write_config1")]), - ?line [{trace,{S,_,Node},call,{?MODULE,foo,[]}}, - end_of_trace, - {trace,Other,call,{?MODULE,foo,[]}}, + ?line [{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}}, + {trace_ts,Other,call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace] = flush(), case metatest(Other,OtherNode,Privdir,"-write_config1.ti") of @@ -410,16 +406,15 @@ write_config2(Config) when is_list(Config) -> ?line ok = ttb:run_config(File), ?line ?MODULE:foo(), ?line rpc:call(OtherNode,?MODULE,foo,[]), - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line ?t:stop_node(OtherNode), ?line ok = ttb:format( [filename:join(Privdir, atom_to_list(Node)++"-write_config2"), filename:join(Privdir, atom_to_list(OtherNode)++"-write_config2")]), - ?line [{trace,{S,_,Node},call,{?MODULE,foo,[]}}, - end_of_trace, - {trace,Other,call,{?MODULE,foo,[]}}, + ?line [{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}}, + {trace_ts,Other,call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace] = flush(), case metatest(Other,OtherNode,Privdir,"-write_config2.ti") of @@ -455,18 +450,18 @@ write_config3(Config) when is_list(Config) -> ?line {ok,[{all,[{matched,_,_},{matched,_,_}]}]} = ttb:p(all,call), ?line {ok,[{matched,_,1},{matched,_,1}]} = ttb:tp(?MODULE,foo,[]), ?line ok = ttb:write_config(File,[1,2]), - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line [_,_] = ttb:list_config(File), ?line ok = ttb:run_config(File), ?line ?MODULE:foo(), ?line rpc:call(OtherNode,?MODULE,foo,[]), - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line ok = ttb:format( [filename:join(Privdir, atom_to_list(Node)++"-write_config3"), filename:join(Privdir, atom_to_list(OtherNode)++"-write_config3")]), - ?line [] = flush(), %foo is not traced + ?line [end_of_trace] = flush(), %foo is not traced ?line ok = ttb:write_config(File,[{ttb,tp,[?MODULE,foo,[]]}], [append]), @@ -474,16 +469,15 @@ write_config3(Config) when is_list(Config) -> ?line ok = ttb:run_config(File), ?line ?MODULE:foo(), ?line rpc:call(OtherNode,?MODULE,foo,[]), - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line ?t:stop_node(OtherNode), ?line ok = ttb:format( [filename:join(Privdir, atom_to_list(Node)++"-write_config3"), filename:join(Privdir, atom_to_list(OtherNode)++"-write_config3")]), - ?line [{trace,{S,_,Node},call,{?MODULE,foo,[]}}, - end_of_trace, - {trace,Other,call,{?MODULE,foo,[]}}, + ?line [{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}}, + {trace_ts,Other,call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace] = flush(), case metatest(Other,OtherNode,Privdir,"-write_config3.ti") of @@ -531,12 +525,12 @@ history(Config) when is_list(Config) -> ?line ?MODULE:foo(), ?line ok = ttb:run_history([3,4]), ?line ?MODULE:foo(), - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line ?t:stop_node(OtherNode), ?line ok = ttb:format( [filename:join(Privdir,atom_to_list(Node)++"-history"), filename:join(Privdir,atom_to_list(OtherNode)++"-history")]), - ?line [{trace,{S,_,Node},call,{?MODULE,foo,[]}}, + ?line [{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace] = flush(), ok. @@ -561,17 +555,16 @@ write_trace_info(Config) when is_list(Config) -> ?line ok = ttb:write_trace_info(mytraceinfo,fun() -> node() end), ?line ?MODULE:foo(), ?line rpc:call(OtherNode,?MODULE,foo,[]), - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line ?t:stop_node(OtherNode), ?line ok = ttb:format( [filename:join(Privdir,atom_to_list(Node)++"-write_trace_info"), filename:join(Privdir, atom_to_list(OtherNode)++"-write_trace_info")], [{handler,{fun otherhandler/4,S}}]), - ?line [{{trace,{S,_,Node},call,{?MODULE,foo,[]}},[Node]}, - {end_of_trace,[Node]}, - {{trace,{_,_,OtherNode},call,{?MODULE,foo,[]}},[OtherNode]}, - {end_of_trace,[OtherNode]}] = flush(), + ?line [{{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}},[Node]}, + {{trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},{_,_,_}},[OtherNode]}, + end_of_trace] = flush(), ok. @@ -593,10 +586,10 @@ seq_trace(Config) when is_list(Config) -> ?line Start = spawn(fun() -> seq() end), ?line timer:sleep(300), - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line ok = ttb:format( [filename:join(Privdir,atom_to_list(Node)++"-seq_trace")]), - ?line [{trace,StartProc,call,{?MODULE,seq,[]}}, + ?line [{trace_ts,StartProc,call,{?MODULE,seq,[]},{_,_,_}}, {seq_trace,0,{send,{0,1},StartProc,P1Proc,{Start,P2}}}, {seq_trace,0,{send,{1,2},P1Proc,P2Proc,{P1,Start}}}, {seq_trace,0,{send,{2,3},P2Proc,StartProc,{P2,P1}}}, @@ -660,12 +653,12 @@ diskless(Config) when is_list(Config) -> ?line rpc:call(RemoteNode,?MODULE,foo,[]), ?line timer:sleep(500), % needed for the IP port to flush - ?line ttb:stop(), + ?line ttb:stop([nofetch]), ?line ?t:stop_node(RemoteNode), ?line ok = ttb:format(filename:join(Privdir, atom_to_list(RemoteNode)++"-diskless")), - ?line [{trace,{_,_,RemoteNode},call,{?MODULE,foo,[]}}, + ?line [{trace_ts,{_,_,RemoteNode},call,{?MODULE,foo,[]},{_,_,_}}, end_of_trace] = flush(), ok. @@ -715,7 +708,7 @@ otp_4967_2(Config) when is_list(Config) -> io:format("11: ~p",[now()]), ?line true = lists:member(heihopp,Msgs), % the heihopp message itself io:format("13: ~p",[now()]), - ?line {value,{trace,_,send,heihopp,{_,otp_4967,Node}}} = + ?line {value,{trace_ts,_,send,heihopp,{_,otp_4967,Node},{_,_,_}}} = lists:keysearch(heihopp,4,Msgs), % trace trace of the heihopp message io:format("14: ~p",[now()]), ?line end_of_trace = lists:last(Msgs), % end of the trace @@ -728,6 +721,9 @@ myhandler(_Fd,Trace,_,Relay) -> Relay ! Trace, Relay. +otherhandler(_Fd,Trace,end_of_trace,Relay) -> + Relay ! end_of_trace, + Relay; otherhandler(_Fd,Trace,TI,Relay) -> {value,{mytraceinfo,I}} = lists:keysearch(mytraceinfo,1,TI), Relay ! {Trace,I}, -- cgit v1.2.3 From 7e3bbc9f57bab238d04545bc0cda70ca0bb0b9f8 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Wed, 16 Mar 2011 12:18:12 +0100 Subject: updated documentation --- lib/observer/doc/src/ttb.xml | 229 ++++++++++++++++++++---- lib/observer/doc/src/ttb_ug.xml | 387 ++++++++++++++++++++++++++++++---------- 2 files changed, 485 insertions(+), 131 deletions(-) (limited to 'lib') diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml index fcaa1c2504..8531be93e3 100644 --- a/lib/observer/doc/src/ttb.xml +++ b/lib/observer/doc/src/ttb.xml @@ -25,11 +25,12 @@ ttb Siri hansen + Bartłomiej Puzoń 1 - 2002-02-25 + 2010-08-13 PA1 ttb.sgml @@ -42,6 +43,35 @@

When using ttb, dbg shall not be used in parallel.

+ + start_trace(Nodes, Patterns, FlagSpec, Opts) -> Result + Start a trace port on each given node. + + Result = see p/2 + Nodes = see tracer/2 + Patterns = [tuple()] + FlagSpec = {Procs, Flags} + Proc = see p/2 + Flags = see p/2 + Opts = see tracer/2 + + +

This function is a shortcut allowing to start a trace with one command. Each + tuple in Patterns is converted to list which is in turn passed to + ttb:tpl. + The call: +ttb:start_trace([Node, OtherNode], +[{mod, foo, []}, {mod, bar, 2}], +{all, call}, +[{file, File}, {handler,{fun myhandler/4, S}}]) + is equivalent to +ttb:start_trace([Node, OtherNode], [{file, File}, {handler,{fun myhandler/4, S}}]), +ttb:tpl(mod, foo, []), +ttb:tpl(mod, bar, 2, []), +ttb:p(all, call) +

+
+
tracer() -> Result This is equivalent to tracer(node()). @@ -49,6 +79,17 @@

This is equivalent to tracer(node()).

+ + tracer(Shortcut) -> Result + Handy shortcuts for common tracing settings + + Shortcut = shell | dbg + + +

shell is equivalent to tracer(node(),[{file, {local, "ttb"}}, shell]).

+

dbg is equivalent to tracer(node(),[{shell, only}]).

+
+
tracer(Nodes) -> Result This is equivalent to tracer(Nodes,[]). @@ -62,14 +103,21 @@ Result = {ok, ActivatedNodes} | {error,Reason} Nodes = atom() | [atom()] | all | existing | new - Opts = [Opt] - Opt = {file,Client} | {handler, FormatHandler} | {process_info,PI} + Opts = Opt | [Opt] + Opt = {file,Client} | {handler, FormatHandler} | {process_info,PI} | + shell | {shell, ShellSpec} | {timer, TimerSpec} | {overload, {MSec, Module, Function}} + | {flush, MSec} | resume | {resume, FetchTimeout} + TimerSpec = MSec | {MSec, StopOpts} + MSec = FetchTimeout = integer() + Module = Function = atom() + StopOpts = see stop/2 Client = File | {local, File} File = Filename | Wrap Filename = string() Wrap = {wrap,Filename} | {wrap,Filename,Size,Count} FormatHandler = See format/2 PI = true | false + ShellSpec = true | false | only

This function starts a file trace port on all given nodes @@ -96,7 +144,70 @@ is the process' registered name its globally registered name, or its initial function. It is possible to turn off this functionality by setting PI = false. -

+

+

The {shell, ShellSpec} option indicates that the trace messages should + be printed on the console as they are received by the tracing + process. This implies {local, File} trace client. If the ShellSpec + is only (instead of true), no trace logs are stored. +

+

The shell option is a shortcut for {shell, true}.

+

The timer option indicates that the trace should be + automatically stopped after MSec milliseconds. StopOpts + are passed to ttb:stop/2 command if specified (default is []). + Note that the timing is approximate, as delays related to + network communication are always present. The timer starts after + ttb:p/2 is issued, so you can set up your trace patterns before. +

+

The overload_protection option allows to enable overload + checking on the nodes under trace. Module:Function(check) + is performed each MSec milliseconds. If the check returns + true, the tracing is disabled on a given node.
+ Module:Function should be able to handle at least three + atoms: init, check and stop. init and + stop give the user a possibility to initialize and clean + up the check environment.
+ When a node gets overloaded, it is not possible to issue ttb:p + nor any command from the ttb:tp family, as it would lead to + inconsistent tracing state (different trace specifications on + different node). +

+

The flush option periodically flushes all file trace + port clients (see dbg:flush_trace_port/1). When enabled, + the buffers are freed each MSec milliseconds. This option is + not allowed with {file, {local, File}} tracing. +

+

{resume, FetchTimeout} enables the autoresume feature. + Whenever enabled, remote nodes try to reconnect to the controlling node + in case they were restarted. The feature requires runtime_tools + application to be started (so it has to be present in the .boot + scripts if the traced nodes run with embedded erlang). If this is + not possible, resume may be performed manually by starting + runtime_tools remotely using rpc:call/4.
+ ttb tries to fetch all logs from a reconnecting node before + reinitializing the trace. This has to finish within FetchTimeout milliseconds + or is aborted
+ By default, autostart information is stored in a file called + ttb_autostart.bin on each node. If this is not desired + (i.e. on diskless nodes), a custom module to handle autostart + information storage and retrieval can be provided by specifying + ttb_autostart_module environment variable for the runtime_tools + application. The module has to respond to the following API: + + write_config(Data) -> ok + Store the provided data for further retrieval. It is + important to realize that the data storage used must not + be affected by the node crash. + read_config() -> {ok, Data} | {error, Error} + Retrieve configuration stored with write_config(Data). + delete_config() -> ok + Delete configuration stored with write_config(Data). + Note that after this call any subsequent calls to read_config + must return {error, Error}. + + +

+

The resume option implies the default FetchTimeout, which is + 10 seconds

@@ -110,7 +221,7 @@

This function sets the given trace flags on the given - processes. + processes. The timestamp flag is always turned on.

Please turn to the Reference manual for module dbg for details about the possible trace flags. The parameter @@ -119,6 +230,9 @@ registered names or process identifiers. If a registered name is given, the flags are set on processes with this name on all active nodes.

+

Issuing this command starts the timer for this trace if + timer option was specified with tracer/2. +

@@ -155,6 +269,18 @@ ctpg Clear trace pattern on global function calls +

With tp and tpl one of match specification shortcuts + may be used (example: ttb:tp(foo_module, caller)). The shortcuts are: + + return - for [{'_',[],[{return_trace}]}] + (report the return value) + caller - for [{'_',[],[{message,{caller}}]}] + (report the calling function) + {codestr, Str} - for dbg:fun2ms/1 arguments + passed as strings (example: "fun(_) -> return_trace() end") + + +

@@ -189,7 +315,7 @@ - write_config(ConfigFile,Config,Opt) -> ok | {error,Reason} + write_config(ConfigFile,Config,Opts) -> ok | {error,Reason} Creates a config file. ConfigFile = string() @@ -197,7 +323,8 @@ Mod = atom() Func = atom() Args = [term()] - Opt = [] | [append] + Opts = Opt | [Opt] + Opt = append

This function creates or extends a config file which can be @@ -213,9 +340,9 @@ should be a list of integers pointing out the entries to be stored.

-

If Opt is not given or if it is [], +

If Opts is not given or if it is [], ConfigFile is deleted and a new file is created. If - Opt = [append], ConfigFile will not be deleted. + Opts = [append], ConfigFile will not be deleted. The new information will be appended at the end of the file.

@@ -226,7 +353,9 @@ ConfigFile = string() -

Executes all entries in the given config file.

+

Executes all entries in the given config file. Note that the history + of the last trace is always available in the file named + ttb_last_config.

@@ -243,6 +372,9 @@

The content of a config file can be listed with list_config/1.

+

Note that the history + of the last trace is always available in the file named + ttb_last_config.

@@ -334,29 +466,52 @@ - stop(Opts) -> stopped + stop(Opts) -> stopped | {stopped, Dir} Stop tracing and fetch/format logs from all nodes - Opts = [Opt] - Opt = fetch | format + Opts = Opt | [Opt] + Opt = nofetch | {fetch_dir, Dir} | format | {format, FormatOpts} | return + Dir = string() + FormatOpts = see format/2 -

Stops tracing on all nodes. -

-

The fetch option indicates that trace logs shall be - collected from all nodes after tracing is stopped. This option - is useful if nodes on remote machines are traced. Logs and - trace information files are then sent to the trace control +

Stops tracing on all nodes. Logs and + trace information files are sent to the trace control node and stored in a directory named - ttb_upload-Timestamp, where Timestamp is on the + ttb_upload_FileName-Timestamp, where Filename is + the one provided with {file, File} during trace setup + and Timestamp is of the form yyyymmdd-hhmmss. Even logs from nodes on the same machine as the trace control node are moved to this directory. -

+ The history list is saved to a file named ttb_last_config + for further reference (as it will be not longer accessible + through history and configuration management functions (like + ttb:list_history/0). +

+

The nofetch option indicates that trace logs shall not be + collected after tracing is stopped. +

+

The {fetch, Dir} option allows to specify the directory + to fetch the data to. If the directory already exists, an + error is thrown. +

The format option indicates that the trace logs shall be formatted after tracing is stopped. Note that this - option also implies the fetch option, i.e. logs are - collected in a new directory on the trace control node before - formatting. All logs in the directory will be merged.

+ fetching the logs. All logs in the fetch directory will be merged. + You may use {format, FormatOpts} to pass additional + arguments to format/2.

+

The return option indicates that the return value + should be {stopped, Dir} and not just stopped. + This implies fetch. +

+
+
+ + get_et_handler() + Returns et handler. + +

The et handler returned by the function may be used with format/2 + or tracer/2. Example: ttb:format(Dir, [{handler, ttb:get_et_handler()}]).

@@ -372,37 +527,37 @@ File = string() | [string()] This can be the name of a binary log, a list of such logs or the name of a directory containing one or more binary logs. - Options = [Opt] - Opt = {out,Out} | {handler,FormatHandler} + Options = Opt | [Opt] + Opt = {out,Out} | {handler,FormatHandler} | disable_sort Out = standard_io | string() - FormatHandler = {Function, InitialState} | et + FormatHandler = {Function, InitialState} Function = fun(Fd,Trace,TraceInfo,State) -> State Fd = standard_io | FileDescriptor This is the file descriptor of the destination file Out Trace = tuple() This is the trace message. Please turn to the Reference manual for the erlangmodule for details. TraceInfo = [{Key,ValueList}] - This includes the keys flags, clientand node, and if handleris given as option to the tracer function, this is also included. In addition all information written with the write_trace_info/2function is included. + This includes the keys flags, client and node, and if handler is given as option to the tracer function, this is also included. In addition all information written with the write_trace_info/2function is included. -

Reads the given binary trace log(s). If a directory or a - list of logs is given and the timestamp flag was set - during tracing, the trace messages from the different logs are - merged according to the timestamps. -

+

Reads the given binary trace log(s). The logs are processed + in the order of their timestamp as long as disable_sort + option is not given. +

If FormatHandler = {Function,InitialState}, Function will be called for each trace message. If - FormatHandler = et, et_viewer in the Event Tracer application (et) is used for presenting the - trace log graphically. ttb provides a few different + FormatHandler = get_et_handler(), et_viewer in + the Event Tracer application (et) is used for presenting + the trace log graphically. ttb provides a few different filters which can be selected from the Filter menu in the et_viewer. If FormatHandler is not given, a default handler is used which presents each trace message as a line of text.

If Out is given, FormatHandler gets the - filedescriptor to Out as the first parameter. + file descriptor to Out as the first parameter.

-

Out is ignored if FormatHandler = et. +

Out is ignored if et format handler is used.

Wrap logs can be formatted one by one or all in one go. To format one of the wrap logs in a set, give the exact name of diff --git a/lib/observer/doc/src/ttb_ug.xml b/lib/observer/doc/src/ttb_ug.xml index 44b7b08fd3..7079e88232 100644 --- a/lib/observer/doc/src/ttb_ug.xml +++ b/lib/observer/doc/src/ttb_ug.xml @@ -4,7 +4,7 @@

- 20022009 + 20022010 Ericsson AB. All Rights Reserved. @@ -48,11 +48,13 @@ Formatting of binary trace logs and merging of logs from multiple nodes. -

Even though the intention of the Trace Tool Builder is to serve - as a base for tailor made trace tools, it is of course possible - to use it directly from the erlang shell. The application only - allows the use of file port tracer, so if you would like would - like to use other types of trace clients you will be better off +

The intention of the Trace Tool Builder is to serve + as a base for tailor made trace tools, but you may use it directly + from the erlang shell (it may mimic dbg behaviour while + still providing useful additions like match specification shortcuts). + The application only + allows the use of file port tracer, so if you would like + to use other types of trace clients you will be better off using dbg directly instead.

@@ -64,14 +66,15 @@ trace flags on the processes you want to trace with ttb:p/2. Then, when the tracing is completed, you must stop the tracer with ttb:stop/0/1 and format the trace log with - ttb:format/1/2. + ttb:format/1/2 (as long as there is anything to format, of + course).

-

ttb:tracer/0/1/2 opens a file trace port on each node - that shall be traced. All trace messages will be written to this - port and end up in a binary file (the binary trace log). +

ttb:tracer/0/1/2 opens a trace port on each node + that shall be traced. By default, trace messages are written + to binary files on remote nodes(the binary trace log).

-

ttb:p/2 specifies which processes that shall be - traced. Trace flags given in this call specifies what to trace on +

ttb:p/2 specifies which processes shall be + traced. Trace flags given in this call specify what to trace on each process. You can call this function several times if you like different trace flags to be set on different processes.

@@ -105,14 +108,15 @@ -export([f/0]). f() -> receive - From when pid(From) -> + From when is_pid(From) -> Now = erlang:now(), From ! {self(),Now} end.

The following example shows the basic use of ttb from the erlang shell. Default options are used both for starting the - tracer and for formatting. This gives a trace log named - Node-ttb, where Node is the name of the node. The + tracer and for formatting (the custom fetch dir is however provided). + This gives a trace log named Node-ttb in the newly-created + directory, where Node is the name of the node. The default handler prints the formatted trace messages in the shell.

(tiger@durin)50> (tiger@durin)50> %% Here I set a trace pattern on erlang:now/0 (tiger@durin)50> %% The trace pattern is a simple match spec -(tiger@durin)50> %% generated by dbg:fun2ms/1. It indicates that -(tiger@durin)50> %% the return value shall be traced. -(tiger@durin)50> MS = dbg:fun2ms(fun(_) -> return_trace() end). -[{'_',[],[{return_trace}]}] -(tiger@durin)51> ttb:tp(erlang,now,MS). +(tiger@durin)50> %% indicating that the return value should be +(tiger@durin)50> %% traced. Refer to the reference_manual for +(tiger@durin)50> %% the full list of match spec shortcuts +(tiger@durin)50> %% available. +(tiger@durin)51> ttb:tp(erlang,now,return). {ok,[{matched,tiger@durin,1},{saved,1}]} (tiger@durin)52> (tiger@durin)52> %% I run my test (i.e. send a message to @@ -145,11 +149,11 @@ f() -> (tiger@durin)53> (tiger@durin)53> %% And then I have to stop ttb in order to flush (tiger@durin)53> %% the trace port buffer -(tiger@durin)53> ttb:stop(). -stopped +(tiger@durin)53> ttb:stop([return, {fetch_dir, "fetch"}]). +{stopped, "fetch"} (tiger@durin)54> (tiger@durin)54> %% Finally I format my trace log -(tiger@durin)54> ttb:format("tiger@durin-ttb"). +(tiger@durin)54> ttb:format("fetch"). ({<0.125.0>,{m,f,0},tiger@durin}) call erlang:now() ({<0.125.0>,{m,f,0},tiger@durin}) returned from erlang:now/0 -> {1031,133451,667611} @@ -167,10 +171,6 @@ ok ]]> -export([start/0,trc/1,stop/0,format/1]). -export([print/4]). -%% Include ms_transform.hrl so that I can use dbg:fun2ms/2 to -%% generate match specifications. --include_lib("stdlib/include/ms_transform.hrl"). - %%% -------------Tool API------------- %%% ---------------------------------- %%% Star the "mydebug" tool @@ -180,28 +180,28 @@ start() -> %% module shall be used as format handler ttb:tracer(all,[{file,"debug_log"},{handler,{{?MODULE,print},0}}]), %% All processes (existing and new) shall trace function calls - %% and include a timestamp in each trace message - ttb:p(all,[call,timestamp]). + %% We want trace messages to be sorted upon format, which requires + %% timestamp flag. The flag is however enabled by default in ttb. + ttb:p(all,call). %%% Set trace pattern on function(s) -trc(M) when atom(M) -> +trc(M) when is_atom(M) -> trc({M,'_','_'}); -trc({M,F}) when atom(M), atom(F) -> +trc({M,F}) when is_atom(M), is_atom(F) -> trc({M,F,'_'}); -trc({M,F,_A}=MFA) when atom(M), atom(F) -> - %% This match spec specifies that return values shall - %% be traced. NOTE that ms_transform.hrl must be included - %% if dbg:fun2ms/1 shall be used! +trc({M,F,_A}=MFA) when is_atom(M), is_atom(F) -> + %% This match spec shortcut specifies that return values shall + %% be traced. MatchSpec = dbg:fun2ms(fun(_) -> return_trace() end), ttb:tpl(MFA,MatchSpec). %%% Format a binary trace log -format(File) -> - ttb:format(File). +format(Dir) -> + ttb:format(Dir). %%% Stop the "mydebug" tool stop() -> - ttb:stop(). + ttb:stop(return). %%% --------Internal functions-------- %%% ---------------------------------- @@ -226,9 +226,9 @@ do_print(Out,{trace_ts,P,return_from,{M,F,A},R,Ts},N) -> [N,Ts,P,M,F,A,R]). ]]>

To distinguish trace logs produced with this tool from other logs, the file option is used in tracer/2. The - logs will therefore be named Node-debug_log, where - Node is the name of the node where the log is produced. -

+ logs will therefore be fetched to a directory named + ttb_upload_debug_log-YYYYMMDD-HHMMSS +

By using the handler option when starting the tracer, the information about how to format the file is stored in the trace information file (.ti). This is not necessary, as @@ -278,12 +278,156 @@ do_print(Out,{trace_ts,P,return_from,{M,F,A},R,Ts},N) -> must be given to the tracer/2 function with the value {local, File}, e.g.

-(trace_control@durin)1> ttb:tracer(mynode@diskless,[{file,{local, -{wrap,"mytrace"}}}]). +(trace_control@durin)1> ttb:tracer(mynode@diskless,{file,{local, +{wrap,"mytrace"}}}). {ok,[mynode@diskless]} +
+ Additional tracing options +

When setting up a trace, several features may be turned on:

+ + time-constrained tracing, + overload protection, + autoresuming. + +
+ Time-constrained tracing +

Sometimes, it may be helpful to enable trace for a + given period of time (i.e. to monitor a system for 24 hours + or half of a second). This may be done by issuing additional + {timer, TimerSpec} option. If TimerSpec has the + form of MSec, the trace is stopped after MSec + milliseconds using ttb:stop/0. If any additional options + are provided (TimerSpec = {MSec, Opts}), ttb:stop/1 + is called instead with Opts as the arguments. The timer + is started with ttb:p/2, so any trace patterns should + be set up before. ttb:start_trace/4 + always sets up all pattern before invoking ttb:p/2. + Note that due to network and processing delays the the period + of tracing is approximate. + The example below shows how to set up a trace which will be + automatically stopped and formatted after 5 seconds +

+(tiger@durin)1>ttb:start_trace([node()], + [{erlang, now,[]}], + {all, call}, + [{timer, {5000, format}}]). + +
+
+ +

When tracing live systems, special care needs to be always taken + not to overload a node with too heavy tracing. ttb provides + the overload option to help to address the problem.

+

{overload, MSec, Module, Function} instructs the ttb backend + (called observer_backend, part of the runtime_tools + application) to perform overload check every MSec milliseconds. + If the check (namely Module:Function(check)) returns + true, tracing is disabled on the selected node.

+

Overload protection activated on one node does not + affect other nodes, where the tracing continues as normal. + ttb:stop/0/1 fetches data from all clients, including everything + that has been collected before overload protection was activated. + Note that + changing trace details (with ttb:p and ttb:tp/tpl...) + once overload protection gets activated in one of the traced + nodes is not permitted in order not to allow trace setup + to be inconsistent between nodes. +

+

Module:Function provided with the overload option must + handle three calls: init, check and stop. init + and stop allows to perform some setup and teardown required by + the check. An overload check module could look like this (note that + check is always called by the same process, so put and + get are possible). +

+-module(overload). +-export([check/1]). + +check(init) -> + Pid = sophisticated_module:start(), + put(pid, Pid); +check(check) -> + get(pid) ! is_overloaded, + receive + Reply -> + Reply + after 5000 -> + true + end; +check(stop) -> + get(pid) ! stop. +
+
+ Autoresume +

It is possible that a node (probably a buggy one, hence traced) + crashes. In order to automatically resume tracing on the node + as soon as it gets back, resume has to be used. When + it is, the failing node tries to reconnect + to trace control node as soon as runtime tools is started. + This implies that runtime_tools must be included in + other node's startup chain (if it is not, one could still + resume tracing by starting runtime_tools manually, + i.e. by an RPC call).

+

In order not to loose the data that the failing node stored + up to the point of crash, the control node will try to fetch + it before restarting trace. This must happen within the allowed + time frame or is aborted (default is 10 seconds, can be customized with + {resume, MSec}). The data fetched this way is then + merged with all other traces.

+

Autostart feature requires additional data to be stored on + traced nodes. By default, the data is stored automatically + to the file called "ttb_autostart.bin" in the traced node's cwd. + Users may decide to change this behaviour (i.e. on diskless + nodes) by specifying their own module to handle autostart data + storage and retrieval (ttb_autostart_module + environment variable of runtime_tools). Please see the + ttb's reference manual to see the module's API. This example + shows the default handler

+ +-module(ttb_autostart). +-export([read_config/0, + write_config/1, + delete_config/0]). + +-define(AUTOSTART_FILENAME, "ttb_autostart.bin"). + +delete_config() -> + file:delete(?AUTOSTART_FILENAME). + +read_config() -> + case file:read_file(?AUTOSTART_FILENAME) of + {ok, Data} -> {ok, binary_to_term(Data)}; + Error -> Error + end. + +write_config(Data) -> + file:write_file(?AUTOSTART_FILENAME, term_to_binary(Data)). + +

Remember that file trace ports buffer the data + by default. If the node crashes, trace messages are not + flushed to the binary log. If the chance of failure is + high, it might be a good idea to automatically flush + the buffers every now and then. Passing {flush, MSec} + as one of ttb:tracer/2 option flushes all buffers + every MSec milliseconds.

+
+
+ dbg mode +

The {shell, ShellType} option allows to make ttb + operation similar to dbg. Using {shell, true} + displays all trace messages in the shell before storing them. + {shell, only} additionally disables message storage + (so that the tool behaves exactly like dbg). This is allowed + only with ip trace ports ({trace, {local, File}}). +

+

The command ttb:tracer(dbg) is a shortcut for the pure-dbg + mode ({shell, only}).

+
+
+
Trace Information and the .ti File @@ -292,13 +436,9 @@ do_print(Out,{trace_ts,P,return_from,{M,F,A},R,Ts},N) -> is the trace information file. It is a binary file, and it contains the process information, trace flags used, the name of the node to which it belongs and all information written with the - write_trace_info/2 function. -

-

To be able to use all this information during formatting, it is - important that the trace information file exists in the same - directory as the trace log, and that it has the same name as the - trace log with the additional extension .ti. -

+ write_trace_info/2 function. .ti files are always fetched + with other logs when the trace is stopped. +

Except for the process information, everything in the trace information file is passed on to the handler function when formatting. The TI parameter is a list of @@ -327,7 +467,12 @@ do_print(Out,{trace_ts,P,return_from,{M,F,A},R,Ts},N) -> each log. ttb will create a new binary log each time a log reaches the maximum size. When the the maximum number of logs are reached, the oldest log is deleted before a new one is created. -

+

+

Note that the overall size of data generated by ttb may be greater + than the wrap specification would suggest - if a traced node restarts + and autoresume is enabled, old wrap log is always stored and + a new one is created. +

Wrap logs can be formatted one by one or all at once. See Formatting.

@@ -348,12 +493,10 @@ do_print(Out,{trace_ts,P,return_from,{M,F,A},R,Ts},N) -> present the trace log graphically (see Presenting trace logs with Event Tracer).

The first argument to ttb:format/1/2 specifies which - binary log(s) to format. This can be the name of one binary log, a - list of such logs or the name of a directory containing one or - more binary logs. If this argument indicates more than one log, - and the timestamp flag was set when tracing, the trace - messages from the different logs will be merged according to the - timestamps in each message. + binary log(s) to format. This is usually the name of a directory + that ttb created during log fetch. Unless there is the disable_sort + option provided, the logs from different files are always sorted + according to timestamp in traces.

The second argument to ttb:format/2 is a list of options. The out option specifies the destination where the @@ -363,7 +506,10 @@ do_print(Out,{trace_ts,P,return_from,{M,F,A},R,Ts},N) -> option is not given, the handler option given when starting the tracer is used. If the handler option was not given when starting the tracer either, a default handler is used, which - prints each trace message as a line of text. + prints each trace message as a line of text. The disable_sort + option indicates that there logs should not be merged according to + timestamp, but processed one file after another (this might be + a bit faster).

A format handler is a fun taking four arguments. This fun will be called for each trace message in the binary log(s). A simple @@ -396,10 +542,24 @@ end handle_gc/4 in the module multitrace.erl which can be found in the src directory of the Observer application.

-

By giving the format handler et, you can have the trace +

The actual trace message is passed as the second argument (Trace). + The possible values of Trace are:

+ + all trace messages described in erlang:trace/3 documentation, + + {drop, N} if ip tracer is used (see dbg:trace_port/2), + + end_of_trace received once when all trace messages have + been processed. + +

By giving the format handler ttb:get_et_handler(), you can have the trace log presented graphically with et_viewer in the Event Tracer application (see Presenting trace logs with Event Tracer). -

+

+

You may always decide not to format the whole trace data contained + in the fetch directory, but analyze single files instead. In order + to do so, a single file (or list of files) have to be passed as + the first argument to format/1/2.

Wrap logs can be formatted one by one or all in one go. To format one of the wrap logs in a set, give the exact name of the file. To format the whole set of wrap logs, give the name with '*' @@ -407,7 +567,7 @@ end

Start tracing:

-(tiger@durin)1> ttb:tracer(node(),[{file,{wrap,"trace"}}]). +(tiger@durin)1> ttb:tracer(node(),{file,{wrap,"trace"}}). {ok,[tiger@durin]} (tiger@durin)2> ttb:p(...) ... @@ -443,7 +603,7 @@ ok to the User's Guide and Reference Manuals for the et application.

-

By giving the format handler et, you can have the +

By giving the format handler ttb:get_et_handler(), you can have the trace log presented graphically with et_viewer in the Event Tracer application. ttb provides a few different filters which can be selected from the Filter menu in the @@ -495,9 +655,23 @@ ok filters respectively, except that each module or function can have several vertical lines, one for each process it resides on.

-

As an example this module is used, and the function - bar:f1() is called from another module foo.

+

In the next example, modules foo and bar are used:

+-module(foo). +-export([start/0,go/0]). + +start() -> + spawn(?MODULE, go, []). + +go() -> + receive + stop -> + ok; + go -> + bar:f1(), + go() + end. + -module(bar). -export([f1/0,f3/0]). f1() -> @@ -506,12 +680,23 @@ f1() -> f2() -> spawn(?MODULE,f3,[]). f3() -> - ok. -

The call and return_to flags are used, and - trace pattern is set on local calls in module bar. -

-

ttb:format("tiger@durin-ttb", [{handler, et}]) gives the - following result: + ok. + +

Now let's set up the trace.

+ +(tiger@durin)1>%%First we retrieve the Pid to limit traced processes set +(tiger@durin)1>Pid = foo:start(). +(tiger@durin)2>%%Now we set up tracing +(tiger@durin)2>ttb:tracer(). +(tiger@durin)3>ttb:p(Pid, [call, return_to, procs, set_on_spawn]). +(tiger@durin)4>ttb:tpl(bar, []). +(tiger@durin)5>%%Invoke our test function and see output with et viewer +(tiger@durin)5>Pid ! go. +(tiger@durin)6>ttb:stop({format, {handler, ttb:get_et_handler()}}). + + +

This shoud render a result similar to the + following:

@@ -520,25 +705,37 @@ f3() -> Filter: "mods_and_procs" + +

Note, that we can use ttb:start_trace/4 function to help + us here:

+ +(tiger@durin)1>Pid = foo:start(). +(tiger@durin)2>ttb:start_trace([node()], + [{bar,[]}], + {Pid, [call, return_to, procs, set_on_spawn]} + {handler, ttb:get_et_handler()}). +(tiger@durin)3>Pid ! go. +(tiger@durin)4>ttb:stop(format). + +
Automatically collect and format logs from all nodes -

If the option fetch is given to the ttb:stop/1 - function, trace logs and trace information files are fetched - from all nodes after tracing is stopped. The logs are stored in a - new directory named ttb_upload-Timestamp under the working - directory of the trace control node. +

By default ttb:stop/1 fetches trace logs and + trace information files from all nodes. The logs are stored in a + new directory named ttb_upload-Filename-Timestamp under the working + directory of the trace control node. Fetching may be disabled by + providing the nofetch option to ttb:stop/1. User can + specify a fetch directory of his choice passing the + {fetch_dir, Dir} option.

If the option format is given to ttb:stop/1, the trace logs are automatically formatted after tracing is - stopped. Note that format also implies fetch, - i.e. the trace logs will be collected from all nodes as for the - fetch option before they are formatted. All logs in the - upload directory are merged during formatting. -

+ stopped. +

@@ -546,13 +743,18 @@ f3() ->

For the tracing functionality, dbg could be used instead of the ttb for setting trace flags on processes and trace patterns for call trace, i.e. the functions p, tp, - tpl, ctp, ctpl and ctpg. The only - thing added by ttb for these functions is that all calls - are stored in the history buffer and can be recalled and stored in - a configuration file. This makes it easy to setup the same trace - environment e.g. if you want to compare two test runs. It also - reduces the amount of typing when using ttb from the erlang - shell. + tpl, ctp, ctpl and ctpg. There are only + two things added by ttb for these functions: + + all calls are stored in the history buffer and can be + recalled and stored in a configuration file. This makes it + easy to setup the same trace environment e.g. if you want to + compare two test runs. It also reduces the amount of + typing when using ttb from the erlang shell; + shortcuts are provided for the most common match + specifications (in order not to force the user to use + dbg:fun2ms continually). +

Use list_history/0 to see the content of the history buffer, and run_history/1 to re-execute one of the entries. @@ -574,7 +776,8 @@ f3() -> selected entries from the history by calling ttb:write_config(ConfigFile,NumList), where NumList is a list of integers pointing out the history - entries to write. + entries to write. Moreover, the history buffer is always dumped + to ttb_last_config when ttb:stop/0/1 is called.

User defined entries can also be written to a config file by calling the function @@ -720,9 +923,7 @@ ok {ok,[{matched,1},{saved,1}]} (tiger@durin)113> dbg:get_tracer(), seq_trace:reset_trace(). true -(tiger@durin)114> ttb:stop(). -ok -(tiger@durin)115> ttb:format("tiger@durin-ttb"). +(tiger@durin)114> ttb:stop(format). ({<0.158.0>,{shell,evaluator,3},tiger@durin}) call dbg:get_tracer() SeqTrace [0]: ({<0.158.0>,{shell,evaluator,3},tiger@durin}) {<0.237.0>,dbg,tiger@durin} ! {<0.158.0>,{get_tracer,tiger@durin}} @@ -743,9 +944,7 @@ ok (tiger@durin)117> seq_trace:set_token(send,true), dbg:get_tracer(), seq_trace:reset_trace(). true -(tiger@durin)118> ttb:stop(). -ok -(tiger@durin)119> ttb:format("tiger@durin-ttb"). +(tiger@durin)118> ttb:stop(format). SeqTrace [0]: ({<0.158.0>,{shell,evaluator,3},tiger@durin}) {<0.246.0>,dbg,tiger@durin} ! {<0.158.0>,{get_tracer,tiger@durin}} [Serial: {0,1}] -- cgit v1.2.3 From 871839b699bcb9b9b6bdbdcb01aa272bdf049882 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Mon, 28 Mar 2011 08:59:27 +0200 Subject: new tests for ttb module --- lib/observer/test/Makefile | 3 + lib/observer/test/client.erl | 29 ++++ lib/observer/test/server.erl | 43 ++++++ lib/observer/test/ttb_SUITE.erl | 303 ++++++++++++++++++++++++++++++++++++++- lib/observer/test/ttb_helper.erl | 152 ++++++++++++++++++++ 5 files changed, 527 insertions(+), 3 deletions(-) create mode 100644 lib/observer/test/client.erl create mode 100644 lib/observer/test/server.erl create mode 100644 lib/observer/test/ttb_helper.erl (limited to 'lib') diff --git a/lib/observer/test/Makefile b/lib/observer/test/Makefile index 6073e6ea00..bf99f07081 100644 --- a/lib/observer/test/Makefile +++ b/lib/observer/test/Makefile @@ -22,7 +22,10 @@ MODULES = \ observer_SUITE \ crashdump_viewer_SUITE \ etop_SUITE \ + ttb_helper \ ttb_SUITE \ + client \ + server \ crashdump_helper ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/observer/test/client.erl b/lib/observer/test/client.erl new file mode 100644 index 0000000000..5d8f0b30df --- /dev/null +++ b/lib/observer/test/client.erl @@ -0,0 +1,29 @@ +-module(client). +-compile(export_all). + +init(Node) -> + io:format(user, "Initing~p~n", [Node]), + application:start(runtime_tools), + net_kernel:connect_node(Node). + +init() -> + init(server_node()). + +restart() -> + init:restart(). + +server_node() -> + {ok,HostName} = inet:gethostname(), + list_to_atom("server@" ++ HostName). + +get() -> + erlang:send({server,server_node()}, {get,self()}), + receive Data -> Data + after 1000 -> no_reply + end. + +put(Thing) -> + erlang:send({server,server_node()}, {put,self(),Thing}), + receive ok -> ok + after 1000 -> no_reply + end. diff --git a/lib/observer/test/server.erl b/lib/observer/test/server.erl new file mode 100644 index 0000000000..c1b1fea562 --- /dev/null +++ b/lib/observer/test/server.erl @@ -0,0 +1,43 @@ +-module(server). +-compile(export_all). + +start() -> + application:start(runtime_tools), + Pid = spawn(?MODULE,loop,[[], 0]), + register(server,Pid). + +stop() -> + case lists:member(server, registered()) of + true -> + server ! stop; + false -> + ok + end. + +loop(Data, Num) -> + receive + {put,From,Ting} -> From ! ok, + received(From,Ting), + loop([Ting|Data], Num+1); + {get,From} -> From ! Data, + loop(Data, Num+1); + stop -> stopped; + clear -> loop([], Num+1); + {cnt, From} -> From ! Num, + loop(Data, Num) + end. + +counter() -> + server ! {cnt, self()}, + receive + Num -> + Num + end. + +received(From, Thing) -> + case Thing of + never_send_this_atom -> + loop(Thing, 0); + _ -> + {return, 27, Thing, From} + end. diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl index dbeee978b1..643a4533cf 100644 --- a/lib/observer/test/ttb_SUITE.erl +++ b/lib/observer/test/ttb_SUITE.erl @@ -1,6 +1,6 @@ -%% %% %CopyrightBegin% %% +%% %% Copyright Ericsson AB 2002-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, @@ -33,9 +33,17 @@ -include_lib("test_server/include/test_server.hrl"). -define(default_timeout, ?t:minutes(1)). +-define(OUTPUT, "handler_output"). +-define(FNAME, "temptest"). +-define(DIRNAME, "ddtemp"). init_per_testcase(_Case, Config) -> ttb:stop(), + os:cmd("rm -rf " ++ ?OUTPUT), + os:cmd("rm -rf ttb_upload*"), + os:cmd("rm -rf " ++ ?DIRNAME), + os:cmd("rm -rf *@*"), + os:cmd("rm -rf ttb_last_config"), ?line Dog=test_server:timetrap(?default_timeout), [{watchdog, Dog}|Config]. end_per_testcase(_Case, Config) -> @@ -49,7 +57,15 @@ all() -> [file, file_no_pi, file_fetch, wrap, wrap_merge, wrap_merge_fetch_format, write_config1, write_config2, write_config3, history, write_trace_info, seq_trace, - diskless, otp_4967_1, otp_4967_2]. + diskless, otp_4967_1, otp_4967_2, + fetch_when_no_option_given, basic_ttb_run_ip_port, basic_ttb_run_file_port, + return_implies_fetch, logfile_name_in_fetch_dir, upload_to_my_logdir, + upload_to_my_existing_logdir, fetch_with_options_not_as_list, + error_when_formatting_multiple_files_4393, format_on_trace_stop, + trace_to_remote_files_on_localhost_with_different_pwd, + trace_to_local_files_on_localhost_with_different_pwd, + trace_to_remote_files_on_localhost_with_different_pwd_abs, + one_command_trace_setup, dbg_style_fetch, shell_tracing_init]. groups() -> []. @@ -721,7 +737,15 @@ myhandler(_Fd,Trace,_,Relay) -> Relay ! Trace, Relay. -otherhandler(_Fd,Trace,end_of_trace,Relay) -> +simple_call_handler() -> + {fun(A, {trace_ts, _, call, _, _} ,_,_) -> io:format(A, "ok.~n", []); + (_, end_of_trace, _, _) -> ok end, []}. + +marking_call_handler() -> + {fun(_, _, _, initial) -> file:write_file("HANDLER_OK", []); + (_,_,_,_) -> ok end, initial}. + +otherhandler(_Fd,_,end_of_trace,Relay) -> Relay ! end_of_trace, Relay; otherhandler(_Fd,Trace,TI,Relay) -> @@ -790,3 +814,276 @@ check_gone(Dir,File) -> false -> ok end. + +start_client_and_server() -> + ?line {ok,ClientNode} = ?t:start_node(client,slave,[]), + ?line ok = ttb_helper:c(code, add_paths, [code:get_path()]), + ?line {ok,ServerNode} = ?t:start_node(server,slave,[]), + ?line ok = ttb_helper:s(code, add_paths, [code:get_path()]), + ?line ttb_helper:clear(), + {ServerNode, ClientNode}. + +begin_trace(ServerNode, ClientNode, Dest) -> + ?line {ok, _} = + ttb:tracer([ServerNode,ClientNode],[{file, Dest}]), + ?line ttb:p(all, call), + ?line ttb:tp(server, received, []), + ?line ttb:tp(client, put, []), + ?line ttb:tp(client, get, []). + +check_size(N, Dest, Output, ServerNode, ClientNode) -> + ?line begin_trace(ServerNode, ClientNode, Dest), + ?line ttb_helper:msgs(N), + ?line {_, D} = ttb:stop([fetch, return]), + ?line ttb:format(D, [{out, Output}, {handler, simple_call_handler()}]), + ?line {ok, Ret} = file:consult(Output), + ?line true = (N + 1 == length(Ret)). + +fetch_when_no_option_given(suite) -> + []; +fetch_when_no_option_given(doc) -> + ["Fetch when no option given"]; +fetch_when_no_option_given(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line {ok, Privdir} = file:get_cwd(), + ?line [] = filelib:wildcard(filename:join(Privdir,"ttb_upload_temptest*")), + begin_trace(ServerNode, ClientNode, ?FNAME), + ?line ttb_helper:msgs(4), + ?line stopped = ttb:stop(), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line [_] = filelib:wildcard(filename:join(Privdir,"ttb_upload_temptest*")). + +basic_ttb_run_ip_port(suite) -> + []; +basic_ttb_run_ip_port(doc) -> + ["Basic ttb run ip port"]; +basic_ttb_run_ip_port(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line check_size(1, {local, ?FNAME}, ?OUTPUT, ServerNode, ClientNode), + ?line check_size(2, {local, ?FNAME}, ?OUTPUT, ServerNode, ClientNode), + ?line check_size(10, {local, ?FNAME}, ?OUTPUT, ServerNode, ClientNode), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode). + +basic_ttb_run_file_port(suite) -> + []; +basic_ttb_run_file_port(doc) -> + ["Basic ttb run file port"]; +basic_ttb_run_file_port(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line check_size(1, ?FNAME, ?OUTPUT, ServerNode, ClientNode), + ?line check_size(2, ?FNAME, ?OUTPUT, ServerNode, ClientNode), + ?line check_size(10, ?FNAME, ?OUTPUT, ServerNode, ClientNode), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode). + +return_implies_fetch(suite) -> + []; +return_implies_fetch(doc) -> + ["Return implies fetch"]; +return_implies_fetch(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace(ServerNode, ClientNode, ?FNAME), + ?line ttb_helper:msgs(2), + ?line {_,_} = ttb:stop([return]), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode). + +logfile_name_in_fetch_dir(suite) -> + []; +logfile_name_in_fetch_dir(doc) -> + ["Logfile name in fetch dir"]; +logfile_name_in_fetch_dir(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace(ServerNode, ClientNode, {local, ?FNAME}), + ?line {_,Dir} = ttb:stop([return]), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line P1 = lists:nth(3, string:tokens(filename:basename(Dir), "_")), + ?line P2 = hd(string:tokens(P1, "-")), + ?line File = P2. + +upload_to_my_logdir(suite) -> + []; +upload_to_my_logdir(doc) -> + ["Upload to my logdir"]; +upload_to_my_logdir(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line {ok, _} = + ttb:tracer([ServerNode,ClientNode],[{file, ?FNAME}]), + ?line {stopped,_} = ttb:stop([return, {fetch_dir, ?DIRNAME}]), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line true = filelib:is_file(?DIRNAME), + ?line [] = filelib:wildcard("ttb_upload_"++?FNAME). + +upload_to_my_existing_logdir(suite) -> + []; +upload_to_my_existing_logdir(doc) -> + ["Upload to my existing logdir"]; +upload_to_my_existing_logdir(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line ok = file:make_dir(?DIRNAME), + ?line {ok, _} = + ttb:tracer([ServerNode,ClientNode],[{file, ?FNAME}]), + ?line {error,_,_} = (catch ttb:stop([return, {fetch_dir, ?DIRNAME}])), + ?line {stopped,_} = ttb:stop(return), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode). + +fetch_with_options_not_as_list(suite) -> + []; +fetch_with_options_not_as_list(doc) -> + ["Fetch with options not as list"]; +fetch_with_options_not_as_list(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line {ok, _} = + ttb:tracer([ServerNode,ClientNode],[{file, ?FNAME}]), + ?line {stopped, D} = ttb:stop(return), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line false = filelib:is_file(?OUTPUT), + ?line ttb:format(D, {out, ?OUTPUT}), + ?line true = filelib:is_file(?OUTPUT). + +error_when_formatting_multiple_files_4393(suite) -> + []; +error_when_formatting_multiple_files_4393(doc) -> + ["Error when formatting multiple files"]; +error_when_formatting_multiple_files_4393(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace(ServerNode, ClientNode, ?FNAME), + ?line ttb_helper:msgs(2), + ?line {_, Dir} = ttb:stop(return), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line Files = [filename:join(Dir, atom_to_list(ttb_helper:get_node(server)) ++ "-" ++ ?FNAME), + filename:join(Dir, atom_to_list(ttb_helper:get_node(client)) ++ "-" ++ ?FNAME)], + ?line ok = ttb:format(Files). + +format_on_trace_stop(suite) -> + []; +format_on_trace_stop(doc) -> + ["Format on trace stop"]; +format_on_trace_stop(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace(ServerNode, ClientNode, {local, ?FNAME}), + ?line ttb_helper:msgs(2), + ?line file:delete("HANDLER_OK"), + ?line {_,_} = ttb:stop([fetch, return, {format, {handler, marking_call_handler()}}]), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line true = filelib:is_file("HANDLER_OK"), + ?line ok = file:delete("HANDLER_OK"). + +trace_to_remote_files_on_localhost_with_different_pwd(suite) -> + []; +trace_to_remote_files_on_localhost_with_different_pwd(doc) -> + ["Trace to remote files on localhost with different pwd"]; +trace_to_remote_files_on_localhost_with_different_pwd(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line ok = file:set_cwd(".."), + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line check_size(2, ?FNAME, ?OUTPUT, ServerNode, ClientNode), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line ok = file:set_cwd(OldDir). + +trace_to_local_files_on_localhost_with_different_pwd(suite) -> + []; +trace_to_local_files_on_localhost_with_different_pwd(doc) -> + ["Trace to local files on localhost with different pwd"]; +trace_to_local_files_on_localhost_with_different_pwd(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line ok = file:set_cwd(".."), + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line check_size(2, {local, ?FNAME}, ?OUTPUT, ServerNode, ClientNode), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line ok = file:set_cwd(OldDir). + +trace_to_remote_files_on_localhost_with_different_pwd_abs(suite) -> + []; +trace_to_remote_files_on_localhost_with_different_pwd_abs(doc) -> + ["Trace to remote files on localhost with different pwd abs"]; +trace_to_remote_files_on_localhost_with_different_pwd_abs(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line ok = file:set_cwd(".."), + ?line {ok, Path} = file:get_cwd(), + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line File = filename:join(Path, ?FNAME), + ?line check_size(2, File, ?OUTPUT, ServerNode, ClientNode), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line ok = file:set_cwd(OldDir). + +one_command_trace_setup(suite) -> + []; +one_command_trace_setup(doc) -> + ["One command trace setup"]; +one_command_trace_setup(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line ttb:start_trace([ttb_helper:get_node(client), ttb_helper:get_node(server)], + [{server, received, '_', []}, + {client, put, 1, []}, + {client, get, '_', []}], + {all, call}, + [{file, ?FNAME}]), + ?line ttb_helper:msgs(2), + ?line {_, D} = ttb:stop(return), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]), + ?line {ok, Ret} = file:consult(?OUTPUT), + ?line 5 = length(Ret). + +dbg_style_fetch(suite) -> + []; +dbg_style_fetch(doc) -> + ["Dbg style fetch"]; +dbg_style_fetch(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line DirSize = length(element(2, file:list_dir("."))), + ?line ttb:start_trace([ttb_helper:get_node(client), ttb_helper:get_node(server)], + [{server, received, '_', []}, + {client, put, 1, []}, + {client, get, '_', []}], + {all, call}, + [{shell, only}]), + ?line DirSize = length(element(2, file:list_dir("."))), + ?line ttb_helper:msgs(2), + ?line DirSize = length(element(2, file:list_dir("."))), + ?line stopped, ttb:stop(format), + %%+1 -> ttb_last_trace + ?line true = (DirSize + 1 == length(element(2, file:list_dir(".")))), + ?line {ok,[{all, [{matched,_,_}, {matched,_,_}]}]} = + ttb:start_trace([ttb_helper:get_node(client), ttb_helper:get_node(server)], + [{server, received, '_', []}, + {client, put, 1, []}, + {client, get, '_', []}], + {all, call}, + [{shell, only}]), + ?line ttb:stop(), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode). + +shell_tracing_init(suite) -> + []; +shell_tracing_init(doc) -> + ["Shell tracing init"]; +shell_tracing_init(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line ttb:tracer([ttb_helper:get_node(client), ttb_helper:get_node(server)], shell), + ?line ttb:stop(), + ?line ttb:tracer([ttb_helper:get_node(client), ttb_helper:get_node(server)], + [{file, {local, ?FNAME}}, shell]), + ?line ttb:stop(), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + try ttb:tracer([ttb_helper:get_node(client), ttb_helper:get_node(server)], + [{file, ?FNAME}, shell]) + catch + exit:local_client_required_on_shell_tracing -> + ok + end. diff --git a/lib/observer/test/ttb_helper.erl b/lib/observer/test/ttb_helper.erl new file mode 100644 index 0000000000..3885812ba1 --- /dev/null +++ b/lib/observer/test/ttb_helper.erl @@ -0,0 +1,152 @@ +-module(ttb_helper). %%Nodes control +-compile(export_all). + +%%API +%%get() -> client:get() +%%put(X) -> client:put(X) +%%msgs(N) -> N times client:put(test_msg) +%%clear() -> restart server +%%ensure_running() / stop() -> start/stop nodes +%%node(atom) -> return atom@hostname + +-define(NODE_CMD(Name), + "erl -sname " ++ atom_to_list(Name) ++ + " -pa .. -pa . -detached -run h send_ok"). +-define(REG_NAME, nc_testing). + +new_fun() -> + fun(_, end_of_trace, _, Dict) -> io:format("~p~n", [dict:to_list(Dict)]); + (_, T, _, Dict) -> case element(2, T) of + {Pid, _, _} -> + dict:update_counter(Pid, 1, Dict); + Pid -> + dict:update_counter(Pid, 1, Dict) + end + end. + +new_fun_2() -> + fun(_, end_of_trace, _, Dict) -> io:format("~p~n", [dict:to_list(Dict)]); + (_, T, _, Dict) -> case element(2, T) of + {_, Name, _} when is_atom(Name)-> + dict:update_counter(Name, 1, Dict); + Pid -> + dict:update_counter(Pid, 1, Dict) + end + + end. + + +ensure_running() -> + try_start_node(server), + try_start_node(client), + clear(). + +try_start_node(Node) -> + global:unregister_name(?REG_NAME), + global:register_name(?REG_NAME, self()), + global:sync(), + N = get_node(Node), + case net_adm:ping(N) of + pong -> + io:format("Node ~p already running~n", [N]); + _ -> + io:format("Starting node ~p... ~p ", [Node, os:cmd(?NODE_CMD(Node))]), + recv() + end. + +clear() -> + s(server, stop, []), + init(). + +stop() -> + s(init, stop, []), + c(init, stop, []). + +msgs(N) -> + [c(client, put, [test_msg]) || _ <- lists:seq(1, N)], + s(server, received, [a,b]), + [dbg:flush_trace_port(Node) || Node <- [client@ariel, server@ariel]]. + +run() -> + ttb({local, "A"}), + msgs(2), + c(erlang, whereis, [ttbt]). + +get() -> c(client, get, []). +put(Thing) -> c(client, put, [Thing]). + +get_node(Node) -> + {ok, Host} = inet:gethostname(), + list_to_atom(atom_to_list(Node) ++ "@" ++ Host). + +trace_setup() -> + ttb:p(all, call), + ttb:tp(server, received, []), + ttb:tp(client, put, []), + ttb:tp(client, get, []). + +ttb() -> ttb("A"). +ttb(File) -> + ttb:tracer([server@ariel, client@ariel], [{file, File}, resume]), + ttb:p(all, [call, timestamp]), + ttb:tp(client, put, []), + ttb:tp(client, get, []), + ttb:tp(server, received, []). + +tc() -> + TC = example_config_gen:create_trace_case("dummy comment"), + Patterns = example_config_gen:create_pattern(client, put, 1, return), + Flags = example_config_gen:create_flags(all, call), + Merge = example_config_gen:create_merge_conf(show_handler(), "dummy merge comment"), + Merge2 = example_config_gen:create_merge_conf(undefined, "dummy merge comment"), + TC2 = example_config_gen:add_pattern(Patterns, TC), + TC3 = example_config_gen:add_flags(Flags, TC2), + TC4 = example_config_gen:add_merge_conf(Merge, TC3), + TC5 = example_config_gen:add_merge_conf(Merge2, TC4), + example_config_gen:add_nodes([get_node(client), get_node(server)], TC5). + + +show(X) -> + io:format(user, "Showing: ~p~n", [X]). + +state_handler() -> + {fun(_,_,I,S) -> io:format(user, "Got from ~p: ~p~n", [I,S]), S+1 end, 0}. + +show_handler() -> + {fun(A,B,_,_) -> io:format(A, "~p~n", [B]) end, []}. + +opts() -> + [[client@ariel, server@ariel], + [{server, received, '_', []}, + {client, put, '_', []}, + {client, get, '_', []}], + {all, call}, + [{file, "TEST"}]]. + +overload_check(check) -> + true; +overload_check(_) -> + ok. +%%%Internal +s(M, F, A) -> rpc:call(get_node(server), M, F, A). +c(M, F, A) -> rpc:call(get_node(client), M, F, A). + +send_ok() -> + pong = net_adm:ping(get_node(test)), + global:sync(), + global:send(?REG_NAME, node()). + +init() -> + True = s(server, start, []), + io:format("ok1: ~p~n", [True]), + true = c(client, init, [get_node(server)]). + +recv() -> + receive + Node -> + io:format("Node ~p ready.~n", [Node]), + ok + after 5000 -> + io:format("Startup failed~n",[]), + throw(startup_failed) + end. -- cgit v1.2.3 From da2e84ecc3c3d537b8464ccd7f2e648ab06a4f86 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Tue, 5 Apr 2011 16:23:21 +0200 Subject: additional tests for ttb module --- lib/observer/test/ttb_SUITE.erl | 182 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 175 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl index 643a4533cf..0c154a796c 100644 --- a/lib/observer/test/ttb_SUITE.erl +++ b/lib/observer/test/ttb_SUITE.erl @@ -65,7 +65,11 @@ all() -> trace_to_remote_files_on_localhost_with_different_pwd, trace_to_local_files_on_localhost_with_different_pwd, trace_to_remote_files_on_localhost_with_different_pwd_abs, - one_command_trace_setup, dbg_style_fetch, shell_tracing_init]. + one_command_trace_setup, dbg_style_fetch, shell_tracing_init, + only_one_state_for_format_handler, only_one_state_with_default_format_handler, + only_one_state_with_initial_format_handler, run_trace_with_shortcut1, + run_trace_with_shortcut2, run_trace_with_shortcut3, run_trace_with_shortcut4, + cant_specify_local_and_flush, trace_sorted_by_default,disable_sorting]. groups() -> []. @@ -745,6 +749,19 @@ marking_call_handler() -> {fun(_, _, _, initial) -> file:write_file("HANDLER_OK", []); (_,_,_,_) -> ok end, initial}. +counter_call_handler() -> + {fun(_, A={trace_ts, _, call, _, _} ,_,State) -> State + 1; + (A, end_of_trace, _, State) -> io:format(A,"~p.~n", [State]) end, 0}. + +ret_caller_call_handler() -> + {fun(A, {trace_ts, _, call, _, _, _} ,_,_) -> io:format(A, "ok.~n", []); + (A, {trace_ts, _, return_from, _, _, _}, _, _) -> io:format(A, "ok.~n", []); + (_, _, _, _) -> ok end, []}. + +node_call_handler() -> + {fun(A, {trace_ts, {_,_,Node}, call, _, _} ,_,_) -> io:format(A, "~p.~n", [Node]); + (_, end_of_trace, _, _) -> ok end, []}. + otherhandler(_Fd,_,end_of_trace,Relay) -> Relay ! end_of_trace, Relay; @@ -831,6 +848,14 @@ begin_trace(ServerNode, ClientNode, Dest) -> ?line ttb:tp(client, put, []), ?line ttb:tp(client, get, []). +begin_trace_local(ServerNode, ClientNode, Dest) -> + ?line {ok, _} = + ttb:tracer([ServerNode,ClientNode],[{file, Dest}]), + ?line ttb:p(all, call), + ?line ttb:tpl(server, received, []), + ?line ttb:tpl(client, put, []), + ?line ttb:tpl(client, get, []). + check_size(N, Dest, Output, ServerNode, ClientNode) -> ?line begin_trace(ServerNode, ClientNode, Dest), ?line ttb_helper:msgs(N), @@ -1081,9 +1106,152 @@ shell_tracing_init(Config) when is_list(Config) -> ?line ttb:stop(), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode), - try ttb:tracer([ttb_helper:get_node(client), ttb_helper:get_node(server)], - [{file, ?FNAME}, shell]) - catch - exit:local_client_required_on_shell_tracing -> - ok - end. + ?line local_client_required_on_shell_tracing = try ttb:tracer([ttb_helper:get_node(client), ttb_helper:get_node(server)], + [{file, ?FNAME}, shell]) + catch + exit:local_client_required_on_shell_tracing -> + local_client_required_on_shell_tracing + end. + +only_one_state_for_format_handler(suite) -> + []; +only_one_state_for_format_handler(doc) -> + ["Only one state for format handler"]; +only_one_state_for_format_handler(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace_local(ServerNode, ClientNode, ?FNAME), + ?line ttb_helper:msgs(2), + ?line {_, D} = ttb:stop([return]), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line ttb:format(D, [{out, ?OUTPUT}, {handler, counter_call_handler()}]), + ?line {ok, Ret} = file:consult(?OUTPUT), + ?line [5] = Ret. + +only_one_state_with_default_format_handler(suite) -> + []; +only_one_state_with_default_format_handler(doc) -> + ["Only one state with default format handler"]; +only_one_state_with_default_format_handler(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace_local(ServerNode, ClientNode, ?FNAME), + ?line ttb_helper:msgs(2), + ?line {_, D} = ttb:stop([return]), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line ttb:format(D, [{out, ?OUTPUT}]), + ?line true = filelib:is_file(?OUTPUT). + +only_one_state_with_initial_format_handler(suite) -> + []; +only_one_state_with_initial_format_handler(doc) -> + ["Only one state with initial format handler"]; +only_one_state_with_initial_format_handler(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line {ok, _} = + ttb:tracer([ServerNode,ClientNode],[{file, ?FNAME}, {handler, counter_call_handler()}]), + ?line ttb:p(all, call), + ?line ttb:tpl(server, received, []), + ?line ttb:tpl(client, put, []), + ?line ttb:tpl(client, get, []), + ?line ttb_helper:msgs(2), + ?line {_, D} = ttb:stop([return]), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line ttb:format(D, [{out, ?OUTPUT}]), + ?line {ok, Ret} = file:consult(?OUTPUT), + ?line [5] = Ret. + +run_trace_with_shortcut(Shortcut, Ret, F) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line {ok, _} = + ttb:tracer([ServerNode,ClientNode],[{file, ?FNAME}]), + ?line ttb:p(all, call), + ?line ttb:F(client, put, Shortcut), + ?line ttb_helper:msgs(2), + ?line {_, D} = ttb:stop([return]), + ?line ttb:format(D, [{out, ?OUTPUT}, {handler, ret_caller_call_handler()}]), + ?line {ok, Ret} =file:consult(?OUTPUT), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode). + +fun_for(return) -> + {codestr, "fun(_) -> return_trace() end"}; +fun_for(msg_false) -> + {codestr, "fun(_) -> message(false) end"}. + +run_trace_with_shortcut1(suite) -> + []; +run_trace_with_shortcut1(doc) -> + ["Run trace with shortcut 1"]; +run_trace_with_shortcut1(Config) when is_list(Config) -> + ?line run_trace_with_shortcut(caller, [ok,ok], tp), + ?line run_trace_with_shortcut(caller, [ok,ok], tpl). + +run_trace_with_shortcut2(suite) -> + []; +run_trace_with_shortcut2(doc) -> + ["Run trace with shortcut 2"]; +run_trace_with_shortcut2(Config) when is_list(Config) -> + ?line run_trace_with_shortcut(return, [ok,ok], tp), + ?line run_trace_with_shortcut(return, [ok,ok], tpl). + +run_trace_with_shortcut3(suite) -> + []; +run_trace_with_shortcut3(doc) -> + ["Run trace with shortcut 3"]; +run_trace_with_shortcut3(Config) when is_list(Config) -> + ?line run_trace_with_shortcut(fun_for(return), [ok,ok], tp), + ?line run_trace_with_shortcut(fun_for(return), [ok,ok], tpl). + +run_trace_with_shortcut4(suite) -> + []; +run_trace_with_shortcut4(doc) -> + ["Run trace with shortcut 4"]; +run_trace_with_shortcut4(Config) when is_list(Config) -> + ?line run_trace_with_shortcut(fun_for(msg_false), [], tp), + ?line run_trace_with_shortcut(fun_for(msg_false), [], tpl). + +cant_specify_local_and_flush(suite) -> + []; +cant_specify_local_and_flush(doc) -> + ["Can't specify local and flush"]; +cant_specify_local_and_flush(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line flush_unsupported_with_ip_trace_port = try ttb:tracer([ServerNode, ClientNode], [{flush, 1000}, {file, {local, ?FNAME}}]) + catch + exit:flush_unsupported_with_ip_trace_port -> + flush_unsupported_with_ip_trace_port + end, + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode). + +trace_sorted_by_default(suite) -> + []; +trace_sorted_by_default(doc) -> + ["Trace sorted by default"]; +trace_sorted_by_default(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace_local(ServerNode, ClientNode, ?FILE), + ?line ttb_helper:msgs(2), + ?line {_, D} = ttb:stop([return]), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line ttb:format(D, [{out, ?OUTPUT}, {handler, node_call_handler()}, {disable_sort, false}]), + {ok, Ret} = file:consult(?OUTPUT), + ?line [ClientNode,ServerNode,ClientNode,ServerNode,ServerNode] = Ret. + +disable_sorting(suite) -> + []; +disable_sorting(doc) -> + ["Disable sorting"]; +disable_sorting(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace_local(ServerNode, ClientNode, ?FILE), + ?line ttb_helper:msgs(2), + ?line {_, D} = ttb:stop([return]), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line ttb:format(D, [{out, ?OUTPUT}, {handler, node_call_handler()}, {disable_sort, true}]), + {ok, Ret} = file:consult(?OUTPUT), + ?line [ClientNode,ClientNode,ServerNode,ServerNode,ServerNode] = Ret. -- cgit v1.2.3 From 4e3b8ccfa74785ca78f70e4d745c123037aada7f Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Tue, 17 May 2011 11:36:45 +0200 Subject: fixed documentation --- lib/observer/doc/src/ttb.xml | 2 +- lib/observer/doc/src/ttb_ug.xml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml index 8531be93e3..c69d465914 100644 --- a/lib/observer/doc/src/ttb.xml +++ b/lib/observer/doc/src/ttb.xml @@ -158,7 +158,7 @@ ttb:p(all, call) network communication are always present. The timer starts after ttb:p/2 is issued, so you can set up your trace patterns before.

-

The overload_protection option allows to enable overload +

The overload option allows to enable overload checking on the nodes under trace. Module:Function(check) is performed each MSec milliseconds. If the check returns true, the tracing is disabled on a given node.
diff --git a/lib/observer/doc/src/ttb_ug.xml b/lib/observer/doc/src/ttb_ug.xml index 7079e88232..4f2b55a22a 100644 --- a/lib/observer/doc/src/ttb_ug.xml +++ b/lib/observer/doc/src/ttb_ug.xml @@ -170,7 +170,9 @@ ok ]]> -module(mydebug). -export([start/0,trc/1,stop/0,format/1]). -export([print/4]). - +%% Include ms_transform.hrl so that I can use dbg:fun2ms/2 to +%% generate match specifications. +-include_lib("stdlib/include/ms_transform.hrl"). %%% -------------Tool API------------- %%% ---------------------------------- %%% Star the "mydebug" tool -- cgit v1.2.3 From 2d319d3fcdc9bf7b8ac7d389ce5ee0a38645882f Mon Sep 17 00:00:00 2001 From: Andrzej Telezynski Date: Thu, 21 Jul 2011 13:08:28 +0200 Subject: It is safe to change cwd on control node or remote nodes while tracing --- lib/observer/src/ttb.erl | 37 +++++++---------- lib/observer/test/ttb_SUITE.erl | 64 ++++++++++++++++++++++++++++++ lib/runtime_tools/src/observer_backend.erl | 14 ++++--- 3 files changed, 87 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index be996512af..342826a482 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -668,18 +668,18 @@ loop(NodeInfo, SessionInfo) -> receive {init_node,Node,MetaFile,PI,Traci} -> erlang:monitor_node(Node,true), - MetaPid = + {AbsoluteMetaFile, MetaPid} = case rpc:call(Node, observer_backend, ttb_init_node, [MetaFile,PI,Traci]) of - {ok,MP} -> - MP; + {ok,MF,MP} -> + {MF,MP}; {badrpc,nodedown} -> %% We will get a nodedown message - undefined + {MetaFile,undefined} end, - loop(dict:store(Node,{MetaFile,MetaPid},NodeInfo), SessionInfo); + loop(dict:store(Node,{AbsoluteMetaFile,MetaPid},NodeInfo), SessionInfo); {get_nodes,Sender} -> Sender ! {?MODULE,dict:fetch_keys(NodeInfo)}, loop(NodeInfo, SessionInfo); @@ -839,23 +839,14 @@ fetch_report(Localhost, Dir, Node, MetaFile) -> fetch(Localhost,Dir,Node,MetaFile) -> case (host(Node) == Localhost) orelse is_local(MetaFile) of - true -> % same host, just move the files + true -> % same host, just move the files Files = get_filenames(Node,MetaFile), lists:foreach( - fun(File0) -> - case MetaFile of - {local, _, _} -> - File = filename:join(Dir,filename:basename(File0)), - file:rename(File0, File); - _ -> - %%Other nodes may still have different CWD - {ok, Cwd} = rpc:call(Node, file, get_cwd, []), - File1 = filename:join(Cwd, File0), - File = filename:join(Dir,filename:basename(File1)), - file:rename(File1,File) - end - end, - Files); + fun(File0) -> + Dest = filename:join(Dir,filename:basename(File0)), + file:rename(File0, Dest) + end, + Files); false -> {ok, LSock} = gen_tcp:listen(0, [binary,{packet,2},{active,false}]), {ok,Port} = inet:port(LSock), @@ -917,13 +908,15 @@ wait_for_fetch(Nodes) -> %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - write_info(Nodes,PI,Traci) -> + {ok, Cwd} = file:get_cwd(), lists:foreach(fun({N,{local,C,_},F}) -> MetaFile = case F of none -> none; F -> - file:delete(F ++ ".ti"), - F ++ ".ti" + AbsFile = filename:join(Cwd, F) ++ ".ti", + file:delete(AbsFile), + AbsFile end, Traci1 = [{node,N},{file,C}|Traci], {ok,Port} = dbg:get_tracer(N), diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl index 0c154a796c..93319d16c8 100644 --- a/lib/observer/test/ttb_SUITE.erl +++ b/lib/observer/test/ttb_SUITE.erl @@ -65,6 +65,8 @@ all() -> trace_to_remote_files_on_localhost_with_different_pwd, trace_to_local_files_on_localhost_with_different_pwd, trace_to_remote_files_on_localhost_with_different_pwd_abs, + changing_cwd_on_control_node, changing_cwd_on_remote_node, + changing_cwd_on_control_node_with_local_trace, one_command_trace_setup, dbg_style_fetch, shell_tracing_init, only_one_state_for_format_handler, only_one_state_with_default_format_handler, only_one_state_with_initial_format_handler, run_trace_with_shortcut1, @@ -1002,6 +1004,8 @@ format_on_trace_stop(Config) when is_list(Config) -> ?line true = filelib:is_file("HANDLER_OK"), ?line ok = file:delete("HANDLER_OK"). +%% The following three tests are for the issue "fixes fetch fail when nodes on the same host +%% have different cwd" trace_to_remote_files_on_localhost_with_different_pwd(suite) -> []; trace_to_remote_files_on_localhost_with_different_pwd(doc) -> @@ -1043,6 +1047,66 @@ trace_to_remote_files_on_localhost_with_different_pwd_abs(Config) when is_list(C ?line ?t:stop_node(ClientNode), ?line ok = file:set_cwd(OldDir). +%% Trace is not affected by changes of cwd on control node or remote nodes during tracing +%% (three tests) +changing_cwd_on_control_node(suite) -> + []; +changing_cwd_on_control_node(doc) -> + ["Changing cwd on control node during tracing is safe"]; +changing_cwd_on_control_node(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace(ServerNode, ClientNode, ?FNAME), + ?line NumMsgs = 3, + ?line ttb_helper:msgs(NumMsgs), + ?line ok = file:set_cwd(".."), + ?line ttb_helper:msgs(NumMsgs), + ?line {_, D} = ttb:stop([fetch, return]), + ?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]), + ?line {ok, Ret} = file:consult(?OUTPUT), + ?line true = (2*(NumMsgs + 1) == length(Ret)), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line ok = file:set_cwd(OldDir). + +changing_cwd_on_control_node_with_local_trace(suite) -> + []; +changing_cwd_on_control_node_with_local_trace(doc) -> + ["Changing cwd on control node during local tracing is safe"]; +changing_cwd_on_control_node_with_local_trace(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace(ServerNode, ClientNode, {local, ?FNAME}), + ?line NumMsgs = 3, + ?line ttb_helper:msgs(NumMsgs), + ?line ok = file:set_cwd(".."), + ?line ttb_helper:msgs(NumMsgs), + ?line {_, D} = ttb:stop([fetch, return]), + ?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]), + ?line {ok, Ret} = file:consult(?OUTPUT), + ?line true = (2*(NumMsgs + 1) == length(Ret)), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode), + ?line ok = file:set_cwd(OldDir). + +changing_cwd_on_remote_node(suite) -> + []; +changing_cwd_on_remote_node(doc) -> + ["Changing cwd on remote node during tracing is safe"]; +changing_cwd_on_remote_node(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace(ServerNode, ClientNode, ?FNAME), + ?line NumMsgs = 2, + ?line ttb_helper:msgs(NumMsgs), + ?line ok = rpc:call(ClientNode, file, set_cwd, [".."]), + ?line ttb_helper:msgs(NumMsgs), + ?line {_, D} = ttb:stop([fetch, return]), + ?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]), + ?line {ok, Ret} = file:consult(?OUTPUT), + ?line true = (2*(NumMsgs + 1) == length(Ret)), + ?line ?t:stop_node(ServerNode), + ?line ?t:stop_node(ClientNode). + one_command_trace_setup(suite) -> []; one_command_trace_setup(doc) -> diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl index 0f428de07a..39d54bf782 100644 --- a/lib/runtime_tools/src/observer_backend.erl +++ b/lib/runtime_tools/src/observer_backend.erl @@ -92,13 +92,15 @@ etop_collect([], Acc) -> Acc. %% %% ttb backend %% -ttb_init_node(MetaFile,PI,Traci) -> +ttb_init_node(MetaFile_0,PI,Traci) -> if - is_list(MetaFile); - is_atom(MetaFile) -> + is_list(MetaFile_0); + is_atom(MetaFile_0) -> + {ok, Cwd} = file:get_cwd(), + MetaFile = filename:join(Cwd, MetaFile_0), file:delete(MetaFile); true -> % {local,_,_} - ok + MetaFile = MetaFile_0 end, Self = self(), MetaPid = spawn(fun() -> ttb_meta_tracer(MetaFile,PI,Self) end), @@ -111,7 +113,7 @@ ttb_init_node(MetaFile,PI,Traci) -> false -> ok end, - {ok,MetaPid}. + {ok,MetaFile,MetaPid}. ttb_write_trace_info(MetaPid,Key,What) -> MetaPid ! {metadata,Key,What}, @@ -287,7 +289,7 @@ ttb_fetch(MetaFile,{Port,Host}) -> send_files({Sock,Host},[File|Files]) -> {ok,Fd} = file:open(File,[raw,read,binary]), - gen_tcp:send(Sock,<<1,(list_to_binary(File))/binary>>), + gen_tcp:send(Sock,<<1,(list_to_binary(filename:basename(File)))/binary>>), send_chunks(Sock,Fd), file:delete(File), send_files({Sock,Host},Files); -- cgit v1.2.3 From e59866033b98e5b790850fa6f14d6bbe50c9c6a6 Mon Sep 17 00:00:00 2001 From: Piotr Dorobisz Date: Mon, 20 Jun 2011 13:56:30 +0200 Subject: update format function's description --- lib/observer/doc/src/ttb.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml index c69d465914..bb8348fa52 100644 --- a/lib/observer/doc/src/ttb.xml +++ b/lib/observer/doc/src/ttb.xml @@ -496,8 +496,7 @@ ttb:p(all, call) error is thrown.

The format option indicates that the trace logs - shall be formatted after tracing is stopped. Note that this - fetching the logs. All logs in the fetch directory will be merged. + shall be formatted after tracing is stopped. All logs in the fetch directory will be merged. You may use {format, FormatOpts} to pass additional arguments to format/2.

The return option indicates that the return value -- cgit v1.2.3 From b61018453100ad669a625fceaace488752978e73 Mon Sep 17 00:00:00 2001 From: Andrzej Telezynski Date: Fri, 22 Jul 2011 22:31:27 +0200 Subject: Stop option 'return' changed to 'return_fetch_dir' --- lib/observer/doc/src/ttb.xml | 4 ++-- lib/observer/src/ttb.erl | 6 ++--- lib/observer/test/ttb_SUITE.erl | 50 ++++++++++++++++++++--------------------- 3 files changed, 30 insertions(+), 30 deletions(-) (limited to 'lib') diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml index bb8348fa52..ec70ba3120 100644 --- a/lib/observer/doc/src/ttb.xml +++ b/lib/observer/doc/src/ttb.xml @@ -470,7 +470,7 @@ ttb:p(all, call) Stop tracing and fetch/format logs from all nodes Opts = Opt | [Opt] - Opt = nofetch | {fetch_dir, Dir} | format | {format, FormatOpts} | return + Opt = nofetch | {fetch_dir, Dir} | format | {format, FormatOpts} | return_fetch_dir Dir = string() FormatOpts = see format/2 @@ -499,7 +499,7 @@ ttb:p(all, call) shall be formatted after tracing is stopped. All logs in the fetch directory will be merged. You may use {format, FormatOpts} to pass additional arguments to format/2.

-

The return option indicates that the return value +

The return_fetch_dir option indicates that the return value should be {stopped, Dir} and not just stopped. This implies fetch.

diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 342826a482..5397799227 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -608,9 +608,9 @@ stop_opts(Opts) -> true -> {format, []}; FOpts -> {format, FOpts} end, - case {FormatData, lists:member(return, Opts)} of + case {FormatData, lists:member(return_fetch_dir, Opts)} of {false, true} -> - {fetch, FetchDir}; % if we specify return, the data should be fetched + {fetch, FetchDir}; % if we specify return_fetch_dir, the data should be fetched {false, false} -> case lists:member(nofetch,Opts) of false -> {fetch, FetchDir}; @@ -630,7 +630,7 @@ ensure_fetch_dir(Dir) -> end. stop_return(R,Opts) -> - case {lists:member(return,Opts),R} of + case {lists:member(return_fetch_dir,Opts),R} of {true,_} -> %%Printout moved out of the ttb loop to avoid occasional deadlock io:format("Stored logs in ~s~n",[element(2, R)]), diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl index 93319d16c8..f5310d2190 100644 --- a/lib/observer/test/ttb_SUITE.erl +++ b/lib/observer/test/ttb_SUITE.erl @@ -59,7 +59,7 @@ all() -> write_config3, history, write_trace_info, seq_trace, diskless, otp_4967_1, otp_4967_2, fetch_when_no_option_given, basic_ttb_run_ip_port, basic_ttb_run_file_port, - return_implies_fetch, logfile_name_in_fetch_dir, upload_to_my_logdir, + return_fetch_dir_implies_fetch, logfile_name_in_fetch_dir, upload_to_my_logdir, upload_to_my_existing_logdir, fetch_with_options_not_as_list, error_when_formatting_multiple_files_4393, format_on_trace_stop, trace_to_remote_files_on_localhost_with_different_pwd, @@ -192,7 +192,7 @@ file_fetch(Config) when is_list(Config) -> ?line ?MODULE:foo(), ?line rpc:call(OtherNode,?MODULE,foo,[]), ?line ?t:capture_start(), - ?line ttb:stop([return]), + ?line ttb:stop([return_fetch_dir]), ?line ?t:capture_stop(), ?line [StoreString] = ?t:capture_get(), ?line UploadDir = @@ -861,7 +861,7 @@ begin_trace_local(ServerNode, ClientNode, Dest) -> check_size(N, Dest, Output, ServerNode, ClientNode) -> ?line begin_trace(ServerNode, ClientNode, Dest), ?line ttb_helper:msgs(N), - ?line {_, D} = ttb:stop([fetch, return]), + ?line {_, D} = ttb:stop([fetch, return_fetch_dir]), ?line ttb:format(D, [{out, Output}, {handler, simple_call_handler()}]), ?line {ok, Ret} = file:consult(Output), ?line true = (N + 1 == length(Ret)). @@ -905,15 +905,15 @@ basic_ttb_run_file_port(Config) when is_list(Config) -> ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode). -return_implies_fetch(suite) -> +return_fetch_dir_implies_fetch(suite) -> []; -return_implies_fetch(doc) -> - ["Return implies fetch"]; -return_implies_fetch(Config) when is_list(Config) -> +return_fetch_dir_implies_fetch(doc) -> + ["Return_fetch_dir implies fetch"]; +return_fetch_dir_implies_fetch(Config) when is_list(Config) -> ?line {ServerNode, ClientNode} = start_client_and_server(), ?line begin_trace(ServerNode, ClientNode, ?FNAME), ?line ttb_helper:msgs(2), - ?line {_,_} = ttb:stop([return]), + ?line {_,_} = ttb:stop([return_fetch_dir]), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode). @@ -924,7 +924,7 @@ logfile_name_in_fetch_dir(doc) -> logfile_name_in_fetch_dir(Config) when is_list(Config) -> ?line {ServerNode, ClientNode} = start_client_and_server(), ?line begin_trace(ServerNode, ClientNode, {local, ?FNAME}), - ?line {_,Dir} = ttb:stop([return]), + ?line {_,Dir} = ttb:stop([return_fetch_dir]), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode), ?line P1 = lists:nth(3, string:tokens(filename:basename(Dir), "_")), @@ -939,7 +939,7 @@ upload_to_my_logdir(Config) when is_list(Config) -> ?line {ServerNode, ClientNode} = start_client_and_server(), ?line {ok, _} = ttb:tracer([ServerNode,ClientNode],[{file, ?FNAME}]), - ?line {stopped,_} = ttb:stop([return, {fetch_dir, ?DIRNAME}]), + ?line {stopped,_} = ttb:stop([return_fetch_dir, {fetch_dir, ?DIRNAME}]), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode), ?line true = filelib:is_file(?DIRNAME), @@ -954,8 +954,8 @@ upload_to_my_existing_logdir(Config) when is_list(Config) -> ?line ok = file:make_dir(?DIRNAME), ?line {ok, _} = ttb:tracer([ServerNode,ClientNode],[{file, ?FNAME}]), - ?line {error,_,_} = (catch ttb:stop([return, {fetch_dir, ?DIRNAME}])), - ?line {stopped,_} = ttb:stop(return), + ?line {error,_,_} = (catch ttb:stop([return_fetch_dir, {fetch_dir, ?DIRNAME}])), + ?line {stopped,_} = ttb:stop(return_fetch_dir), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode). @@ -967,7 +967,7 @@ fetch_with_options_not_as_list(Config) when is_list(Config) -> ?line {ServerNode, ClientNode} = start_client_and_server(), ?line {ok, _} = ttb:tracer([ServerNode,ClientNode],[{file, ?FNAME}]), - ?line {stopped, D} = ttb:stop(return), + ?line {stopped, D} = ttb:stop(return_fetch_dir), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode), ?line false = filelib:is_file(?OUTPUT), @@ -982,7 +982,7 @@ error_when_formatting_multiple_files_4393(Config) when is_list(Config) -> ?line {ServerNode, ClientNode} = start_client_and_server(), ?line begin_trace(ServerNode, ClientNode, ?FNAME), ?line ttb_helper:msgs(2), - ?line {_, Dir} = ttb:stop(return), + ?line {_, Dir} = ttb:stop(return_fetch_dir), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode), ?line Files = [filename:join(Dir, atom_to_list(ttb_helper:get_node(server)) ++ "-" ++ ?FNAME), @@ -998,7 +998,7 @@ format_on_trace_stop(Config) when is_list(Config) -> ?line begin_trace(ServerNode, ClientNode, {local, ?FNAME}), ?line ttb_helper:msgs(2), ?line file:delete("HANDLER_OK"), - ?line {_,_} = ttb:stop([fetch, return, {format, {handler, marking_call_handler()}}]), + ?line {_,_} = ttb:stop([fetch, return_fetch_dir, {format, {handler, marking_call_handler()}}]), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode), ?line true = filelib:is_file("HANDLER_OK"), @@ -1061,7 +1061,7 @@ changing_cwd_on_control_node(Config) when is_list(Config) -> ?line ttb_helper:msgs(NumMsgs), ?line ok = file:set_cwd(".."), ?line ttb_helper:msgs(NumMsgs), - ?line {_, D} = ttb:stop([fetch, return]), + ?line {_, D} = ttb:stop([fetch, return_fetch_dir]), ?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]), ?line {ok, Ret} = file:consult(?OUTPUT), ?line true = (2*(NumMsgs + 1) == length(Ret)), @@ -1081,7 +1081,7 @@ changing_cwd_on_control_node_with_local_trace(Config) when is_list(Config) -> ?line ttb_helper:msgs(NumMsgs), ?line ok = file:set_cwd(".."), ?line ttb_helper:msgs(NumMsgs), - ?line {_, D} = ttb:stop([fetch, return]), + ?line {_, D} = ttb:stop([fetch, return_fetch_dir]), ?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]), ?line {ok, Ret} = file:consult(?OUTPUT), ?line true = (2*(NumMsgs + 1) == length(Ret)), @@ -1100,7 +1100,7 @@ changing_cwd_on_remote_node(Config) when is_list(Config) -> ?line ttb_helper:msgs(NumMsgs), ?line ok = rpc:call(ClientNode, file, set_cwd, [".."]), ?line ttb_helper:msgs(NumMsgs), - ?line {_, D} = ttb:stop([fetch, return]), + ?line {_, D} = ttb:stop([fetch, return_fetch_dir]), ?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]), ?line {ok, Ret} = file:consult(?OUTPUT), ?line true = (2*(NumMsgs + 1) == length(Ret)), @@ -1120,7 +1120,7 @@ one_command_trace_setup(Config) when is_list(Config) -> {all, call}, [{file, ?FNAME}]), ?line ttb_helper:msgs(2), - ?line {_, D} = ttb:stop(return), + ?line {_, D} = ttb:stop(return_fetch_dir), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode), ?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]), @@ -1185,7 +1185,7 @@ only_one_state_for_format_handler(Config) when is_list(Config) -> ?line {ServerNode, ClientNode} = start_client_and_server(), ?line begin_trace_local(ServerNode, ClientNode, ?FNAME), ?line ttb_helper:msgs(2), - ?line {_, D} = ttb:stop([return]), + ?line {_, D} = ttb:stop([return_fetch_dir]), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode), ?line ttb:format(D, [{out, ?OUTPUT}, {handler, counter_call_handler()}]), @@ -1200,7 +1200,7 @@ only_one_state_with_default_format_handler(Config) when is_list(Config) -> ?line {ServerNode, ClientNode} = start_client_and_server(), ?line begin_trace_local(ServerNode, ClientNode, ?FNAME), ?line ttb_helper:msgs(2), - ?line {_, D} = ttb:stop([return]), + ?line {_, D} = ttb:stop([return_fetch_dir]), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode), ?line ttb:format(D, [{out, ?OUTPUT}]), @@ -1219,7 +1219,7 @@ only_one_state_with_initial_format_handler(Config) when is_list(Config) -> ?line ttb:tpl(client, put, []), ?line ttb:tpl(client, get, []), ?line ttb_helper:msgs(2), - ?line {_, D} = ttb:stop([return]), + ?line {_, D} = ttb:stop([return_fetch_dir]), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode), ?line ttb:format(D, [{out, ?OUTPUT}]), @@ -1233,7 +1233,7 @@ run_trace_with_shortcut(Shortcut, Ret, F) -> ?line ttb:p(all, call), ?line ttb:F(client, put, Shortcut), ?line ttb_helper:msgs(2), - ?line {_, D} = ttb:stop([return]), + ?line {_, D} = ttb:stop([return_fetch_dir]), ?line ttb:format(D, [{out, ?OUTPUT}, {handler, ret_caller_call_handler()}]), ?line {ok, Ret} =file:consult(?OUTPUT), ?line ?t:stop_node(ServerNode), @@ -1298,7 +1298,7 @@ trace_sorted_by_default(Config) when is_list(Config) -> ?line {ServerNode, ClientNode} = start_client_and_server(), ?line begin_trace_local(ServerNode, ClientNode, ?FILE), ?line ttb_helper:msgs(2), - ?line {_, D} = ttb:stop([return]), + ?line {_, D} = ttb:stop([return_fetch_dir]), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode), ?line ttb:format(D, [{out, ?OUTPUT}, {handler, node_call_handler()}, {disable_sort, false}]), @@ -1313,7 +1313,7 @@ disable_sorting(Config) when is_list(Config) -> ?line {ServerNode, ClientNode} = start_client_and_server(), ?line begin_trace_local(ServerNode, ClientNode, ?FILE), ?line ttb_helper:msgs(2), - ?line {_, D} = ttb:stop([return]), + ?line {_, D} = ttb:stop([return_fetch_dir]), ?line ?t:stop_node(ServerNode), ?line ?t:stop_node(ClientNode), ?line ttb:format(D, [{out, ?OUTPUT}, {handler, node_call_handler()}, {disable_sort, true}]), -- cgit v1.2.3 From 7f2123bc98992fc1a714f373c6416cdb6f6e8697 Mon Sep 17 00:00:00 2001 From: Andrzej Telezynski Date: Sun, 21 Aug 2011 17:18:19 +0200 Subject: Test for: (BUGFIX) Error with ip tracing to wrap sets. --- lib/observer/test/ttb_SUITE.erl | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl index f5310d2190..5c85f143f9 100644 --- a/lib/observer/test/ttb_SUITE.erl +++ b/lib/observer/test/ttb_SUITE.erl @@ -57,7 +57,7 @@ all() -> [file, file_no_pi, file_fetch, wrap, wrap_merge, wrap_merge_fetch_format, write_config1, write_config2, write_config3, history, write_trace_info, seq_trace, - diskless, otp_4967_1, otp_4967_2, + diskless, diskless_wrap, otp_4967_1, otp_4967_2, fetch_when_no_option_given, basic_ttb_run_ip_port, basic_ttb_run_file_port, return_fetch_dir_implies_fetch, logfile_name_in_fetch_dir, upload_to_my_logdir, upload_to_my_existing_logdir, fetch_with_options_not_as_list, @@ -684,6 +684,32 @@ diskless(Config) when is_list(Config) -> end_of_trace] = flush(), ok. +diskless_wrap(suite) -> + []; +diskless_wrap(doc) -> + ["Start tracing on diskless remote node, save to local wrapped file"]; +diskless_wrap(Config) when is_list(Config) -> + ?line {ok,RemoteNode} = ?t:start_node(node2,slave,[]), + ?line c:nl(?MODULE), + ?line S = self(), + ?line Privdir=?config(priv_dir, Config), + ?line File = filename:join(Privdir,"diskless"), + ?line {ok,[RemoteNode]} = + ttb:tracer([RemoteNode],[{file, {local, {wrap,File,200,3}}}, + {handler,{fun myhandler/4, S}}]), + ?line {ok,[{all,[{matched,RemoteNode,_}]}]} = ttb:p(all,call), + ?line {ok,[{matched,RemoteNode,1}]} = ttb:tp(?MODULE,foo,[]), + + ?line rpc:call(RemoteNode,?MODULE,foo,[]), + ?line timer:sleep(500), % needed for the IP port to flush + ?line ttb:stop([nofetch]), + ?line ?t:stop_node(RemoteNode), + ?line ok = ttb:format(filename:join(Privdir, + atom_to_list(RemoteNode)++"-diskless.*.wrp")), + + ?line [{trace_ts,{_,_,RemoteNode},call,{?MODULE,foo,[]},{_,_,_}}, + end_of_trace] = flush(), + ok. otp_4967_1(suite) -> []; -- cgit v1.2.3 From 584eec8da18fe5d33e8d06f12e5bc9367147a252 Mon Sep 17 00:00:00 2001 From: Andrzej Telezynski Date: Sun, 21 Aug 2011 19:57:43 +0200 Subject: Documented global state when applying formatting function to trace logs. --- lib/observer/doc/src/ttb.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml index ec70ba3120..cf1763707d 100644 --- a/lib/observer/doc/src/ttb.xml +++ b/lib/observer/doc/src/ttb.xml @@ -25,7 +25,7 @@ ttb Siri hansen - Bartłomiej Puzoń + Bartlomiej Puzon 1 @@ -553,6 +553,9 @@ ttb:p(all, call) default handler is used which presents each trace message as a line of text.

+

The state returned from each call of Function is passed to the next call, + even if next call is to format a message from another log file. +

If Out is given, FormatHandler gets the file descriptor to Out as the first parameter.

-- cgit v1.2.3 From 224f4324615b05e78b0a5cd3d26f8392b749086c Mon Sep 17 00:00:00 2001 From: Andrzej Telezynski Date: Sun, 21 Aug 2011 20:06:02 +0200 Subject: Removed unused option to send realtime trace to erlide. --- lib/observer/src/ttb.erl | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 5397799227..a0fbe0d60e 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -1274,14 +1274,8 @@ ip_to_file(Trace,{Port, ShellOutput}) -> show_trace(Trace, true) -> dbg:dhandler(Trace, standard_io); -show_trace(_Trace, false) -> - ok; -show_trace(Trace, Pid) when is_pid(Pid) -> - %%This is only to enable erlide to build trace views in real time. - %%Sending trace data to handlers in real time has however - %%to be considered an interesting feature and should be - %%implemented in a generic way in the future - Pid ! {trace, Trace}. +show_trace(_, _) -> + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% For debugging -- cgit v1.2.3 From f7dc3bf45367fb22ababb85a783c2e9e9a1ada00 Mon Sep 17 00:00:00 2001 From: Andrzej Telezynski Date: Tue, 23 Aug 2011 22:14:55 +0200 Subject: Print path to logs dir unless nofetch option specified. --- lib/observer/src/ttb.erl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index a0fbe0d60e..072aa165e7 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -596,6 +596,15 @@ stop(Opts) when is_list(Opts) -> ?MODULE ! {stop,Fetch,self()}, receive {?MODULE,R} -> R end end, + case {Fetch, Result} of + {nofetch, _} -> + ok; + {_, {stopped, _}} -> + %% Printout moved out of the ttb loop to avoid occasional deadlock + io:format("Stored logs in ~s~n", [element(2, Result)]); + {_, _} -> + ok + end, stop_return(Result,Opts); stop(Opts) -> stop([Opts]). @@ -632,8 +641,6 @@ ensure_fetch_dir(Dir) -> stop_return(R,Opts) -> case {lists:member(return_fetch_dir,Opts),R} of {true,_} -> - %%Printout moved out of the ttb loop to avoid occasional deadlock - io:format("Stored logs in ~s~n",[element(2, R)]), R; {false,{stopped,_}} -> stopped; -- cgit v1.2.3 From 1924817788f381da1b8356e336d62f4687717ee4 Mon Sep 17 00:00:00 2001 From: Andrzej Telezynski Date: Wed, 24 Aug 2011 23:56:37 +0200 Subject: More robust testing of tracing with 'local' option. --- lib/observer/test/client.erl | 1 - lib/observer/test/ttb_SUITE.erl | 11 ++++++++--- lib/observer/test/ttb_helper.erl | 13 +++++++++---- 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/observer/test/client.erl b/lib/observer/test/client.erl index 5d8f0b30df..e756f9d6e8 100644 --- a/lib/observer/test/client.erl +++ b/lib/observer/test/client.erl @@ -2,7 +2,6 @@ -compile(export_all). init(Node) -> - io:format(user, "Initing~p~n", [Node]), application:start(runtime_tools), net_kernel:connect_node(Node). diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl index 5c85f143f9..4c7010e427 100644 --- a/lib/observer/test/ttb_SUITE.erl +++ b/lib/observer/test/ttb_SUITE.erl @@ -778,7 +778,7 @@ marking_call_handler() -> (_,_,_,_) -> ok end, initial}. counter_call_handler() -> - {fun(_, A={trace_ts, _, call, _, _} ,_,State) -> State + 1; + {fun(_, {trace_ts, _, call, _, _} ,_,State) -> State + 1; (A, end_of_trace, _, State) -> io:format(A,"~p.~n", [State]) end, 0}. ret_caller_call_handler() -> @@ -886,7 +886,12 @@ begin_trace_local(ServerNode, ClientNode, Dest) -> check_size(N, Dest, Output, ServerNode, ClientNode) -> ?line begin_trace(ServerNode, ClientNode, Dest), - ?line ttb_helper:msgs(N), + ?line case Dest of + {local, _} -> + ?line ttb_helper:msgs_ip(N); + _ -> + ?line ttb_helper:msgs(N) + end, ?line {_, D} = ttb:stop([fetch, return_fetch_dir]), ?line ttb:format(D, [{out, Output}, {handler, simple_call_handler()}]), ?line {ok, Ret} = file:consult(Output), @@ -955,7 +960,7 @@ logfile_name_in_fetch_dir(Config) when is_list(Config) -> ?line ?t:stop_node(ClientNode), ?line P1 = lists:nth(3, string:tokens(filename:basename(Dir), "_")), ?line P2 = hd(string:tokens(P1, "-")), - ?line File = P2. + ?line _File = P2. upload_to_my_logdir(suite) -> []; diff --git a/lib/observer/test/ttb_helper.erl b/lib/observer/test/ttb_helper.erl index 3885812ba1..570ce934ab 100644 --- a/lib/observer/test/ttb_helper.erl +++ b/lib/observer/test/ttb_helper.erl @@ -7,7 +7,7 @@ %%msgs(N) -> N times client:put(test_msg) %%clear() -> restart server %%ensure_running() / stop() -> start/stop nodes -%%node(atom) -> return atom@hostname +%%get_node(atom) -> return atom@hostname -define(NODE_CMD(Name), "erl -sname " ++ atom_to_list(Name) ++ @@ -65,7 +65,12 @@ stop() -> msgs(N) -> [c(client, put, [test_msg]) || _ <- lists:seq(1, N)], s(server, received, [a,b]), - [dbg:flush_trace_port(Node) || Node <- [client@ariel, server@ariel]]. + [dbg:flush_trace_port(Node) || Node <- [get_node(client), get_node(server)]]. + +msgs_ip(N) -> + [c(client, put, [test_msg]) || _ <- lists:seq(1, N)], + s(server, received, [a,b]), + timer:sleep(100). %% allow trace messages to arrive over tcp/ip run() -> ttb({local, "A"}), @@ -87,7 +92,7 @@ trace_setup() -> ttb() -> ttb("A"). ttb(File) -> - ttb:tracer([server@ariel, client@ariel], [{file, File}, resume]), + ttb:tracer([get_node(client), get_node(server)], [{file, File}, resume]), ttb:p(all, [call, timestamp]), ttb:tp(client, put, []), ttb:tp(client, get, []), @@ -116,7 +121,7 @@ show_handler() -> {fun(A,B,_,_) -> io:format(A, "~p~n", [B]) end, []}. opts() -> - [[client@ariel, server@ariel], + [[get_node(client), get_node(server)], [{server, received, '_', []}, {client, put, '_', []}, {client, get, '_', []}], -- cgit v1.2.3 From 7e2c8763c225dc16f98cc5746fa141b970a6aaf3 Mon Sep 17 00:00:00 2001 From: Andrzej Telezynski Date: Thu, 25 Aug 2011 00:45:07 +0200 Subject: Support for stopped and resumed tracing. --- lib/observer/test/ttb_SUITE.erl | 86 +++++++++++++++++++++- lib/observer/test/ttb_helper.erl | 2 +- lib/runtime_tools/src/Makefile | 3 +- lib/runtime_tools/src/observer_backend.erl | 109 +++++++++++++++++++++++----- lib/runtime_tools/src/runtime_tools.app.src | 3 +- lib/runtime_tools/src/runtime_tools_sup.erl | 4 +- 6 files changed, 182 insertions(+), 25 deletions(-) (limited to 'lib') diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl index 4c7010e427..1fd8b4c892 100644 --- a/lib/observer/test/ttb_SUITE.erl +++ b/lib/observer/test/ttb_SUITE.erl @@ -71,7 +71,11 @@ all() -> only_one_state_for_format_handler, only_one_state_with_default_format_handler, only_one_state_with_initial_format_handler, run_trace_with_shortcut1, run_trace_with_shortcut2, run_trace_with_shortcut3, run_trace_with_shortcut4, - cant_specify_local_and_flush, trace_sorted_by_default,disable_sorting]. + cant_specify_local_and_flush, trace_sorted_by_default,disable_sorting, + trace_resumed_after_node_restart, trace_resumed_after_node_restart_ip, + trace_resumed_after_node_restart_wrap, + trace_resumed_after_node_restart_wrap_mult +]. groups() -> []. @@ -1027,7 +1031,7 @@ format_on_trace_stop(doc) -> format_on_trace_stop(Config) when is_list(Config) -> ?line {ServerNode, ClientNode} = start_client_and_server(), ?line begin_trace(ServerNode, ClientNode, {local, ?FNAME}), - ?line ttb_helper:msgs(2), + ?line ttb_helper:msgs_ip(2), ?line file:delete("HANDLER_OK"), ?line {_,_} = ttb:stop([fetch, return_fetch_dir, {format, {handler, marking_call_handler()}}]), ?line ?t:stop_node(ServerNode), @@ -1109,9 +1113,9 @@ changing_cwd_on_control_node_with_local_trace(Config) when is_list(Config) -> ?line {ServerNode, ClientNode} = start_client_and_server(), ?line begin_trace(ServerNode, ClientNode, {local, ?FNAME}), ?line NumMsgs = 3, - ?line ttb_helper:msgs(NumMsgs), + ?line ttb_helper:msgs_ip(NumMsgs), ?line ok = file:set_cwd(".."), - ?line ttb_helper:msgs(NumMsgs), + ?line ttb_helper:msgs_ip(NumMsgs), ?line {_, D} = ttb:stop([fetch, return_fetch_dir]), ?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]), ?line {ok, Ret} = file:consult(?OUTPUT), @@ -1350,3 +1354,77 @@ disable_sorting(Config) when is_list(Config) -> ?line ttb:format(D, [{out, ?OUTPUT}, {handler, node_call_handler()}, {disable_sort, true}]), {ok, Ret} = file:consult(?OUTPUT), ?line [ClientNode,ClientNode,ServerNode,ServerNode,ServerNode] = Ret. + +%% ----------------------------------------------------------------------------- +%% tests for autoresume of tracing +%% ----------------------------------------------------------------------------- + +trace_resumed_after_node_restart(suite) -> + []; +trace_resumed_after_node_restart(doc) -> + ["Test trace resumed after node restart, trace to files on remote node."]; +trace_resumed_after_node_restart(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace_with_resume(ServerNode, ClientNode, ?FNAME), + ?line logic(2,6,file). + +trace_resumed_after_node_restart_ip(suite) -> + []; +trace_resumed_after_node_restart_ip(doc) -> + ["Test trace resumed after node restart, trace via tcp/ip to local node."]; +trace_resumed_after_node_restart_ip(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace_with_resume(ServerNode, ClientNode, {local, ?FNAME}), + ?line logic(2,6,local). + +trace_resumed_after_node_restart_wrap(suite) -> + []; +trace_resumed_after_node_restart_wrap(doc) -> + ["Test trace resumed after node restart, wrap option."]; +trace_resumed_after_node_restart_wrap(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace_with_resume(ServerNode, ClientNode, {wrap, ?FNAME, 10, 4}), + ?line logic(1,4,file). + +trace_resumed_after_node_restart_wrap_mult(suite) -> + []; +trace_resumed_after_node_restart_wrap_mult(doc) -> + ["Test trace resumed after node restart, wrap option, multiple files."]; +trace_resumed_after_node_restart_wrap_mult(Config) when is_list(Config) -> + ?line {ServerNode, ClientNode} = start_client_and_server(), + ?line begin_trace_with_resume(ServerNode, ClientNode, {wrap, ?FNAME, 10, 4}), + ?line logic(20,8,file). + +logic(N, M, TracingType) -> + helper_msgs(N, TracingType), + ?t:stop_node(ttb_helper:get_node(client)), + timer:sleep(2500), + ?line {ok,ClientNode} = ?t:start_node(client,slave,[]), + ?line ok = ttb_helper:c(code, add_paths, [code:get_path()]), + ?line ttb_helper:c(client, init, []), + ?line helper_msgs(N, TracingType), + ?line {_, D} = ttb:stop([return_fetch_dir]), + ?line ?t:stop_node(ttb_helper:get_node(server)), + ?line ?t:stop_node(ClientNode), + ?line ttb:format(D, [{out, ?OUTPUT}, {handler, ret_caller_call_handler2()}]), + ?line {ok, Ret} = file:consult(?OUTPUT), + ?line M = length(Ret). + +begin_trace_with_resume(ServerNode, ClientNode, Dest) -> + ?line {ok, _} = ttb:tracer([ServerNode,ClientNode], [{file, Dest}, resume]), + ?line ttb:p(all, [call, timestamp]), + ?line ttb:tp(server, received, []), + ?line ttb:tp(client, put, []), + ?line ttb:tp(client, get, []). + +ret_caller_call_handler2() -> + {fun(A, {trace_ts, _, call, _, _} ,_,_) -> io:format(A, "ok.~n", []); + (_, _, _, _) -> ok end, []}. + +helper_msgs(N, TracingType) -> + case TracingType of + local -> + ttb_helper:msgs_ip(N); + _ -> + ttb_helper:msgs(N) + end. diff --git a/lib/observer/test/ttb_helper.erl b/lib/observer/test/ttb_helper.erl index 570ce934ab..19fdc0e159 100644 --- a/lib/observer/test/ttb_helper.erl +++ b/lib/observer/test/ttb_helper.erl @@ -11,7 +11,7 @@ -define(NODE_CMD(Name), "erl -sname " ++ atom_to_list(Name) ++ - " -pa .. -pa . -detached -run h send_ok"). + " -pa .. -pa . -detached -run ttb_helper send_ok"). -define(REG_NAME, nc_testing). new_fun() -> diff --git a/lib/runtime_tools/src/Makefile b/lib/runtime_tools/src/Makefile index 4f831f3dd8..46b570210a 100644 --- a/lib/runtime_tools/src/Makefile +++ b/lib/runtime_tools/src/Makefile @@ -46,7 +46,8 @@ MODULES= \ runtime_tools_sup \ dbg \ percept_profile \ - observer_backend + observer_backend \ + ttb_autostart HRL_FILES= ../include/observer_backend.hrl ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl index 39d54bf782..9c1f9da5b1 100644 --- a/lib/runtime_tools/src/observer_backend.erl +++ b/lib/runtime_tools/src/observer_backend.erl @@ -31,6 +31,7 @@ ttb_write_binary/2, ttb_stop/1, ttb_fetch/2, + ttb_resume_trace/0, ttb_get_filenames/1]). -define(CHUNKSIZE,8191). % 8 kbytes - 1 byte @@ -102,8 +103,12 @@ ttb_init_node(MetaFile_0,PI,Traci) -> true -> % {local,_,_} MetaFile = MetaFile_0 end, + case proplists:get_value(resume, Traci) of + {true, _} -> (autostart_module()):write_config(Traci); + _ -> ok + end, Self = self(), - MetaPid = spawn(fun() -> ttb_meta_tracer(MetaFile,PI,Self) end), + MetaPid = spawn(fun() -> ttb_meta_tracer(MetaFile,PI,Self,Traci) end), receive {MetaPid,started} -> ok end, MetaPid ! {metadata,Traci}, case PI of @@ -119,7 +124,8 @@ ttb_write_trace_info(MetaPid,Key,What) -> MetaPid ! {metadata,Key,What}, ok. -ttb_meta_tracer(MetaFile,PI,Parent) -> +ttb_meta_tracer(MetaFile,PI,Parent,SessionData) -> + erlang:monitor(process, proplists:get_value(ttb_control, SessionData)), case PI of true -> ReturnMS = [{'_',[],[{return_trace}]}], @@ -132,22 +138,29 @@ ttb_meta_tracer(MetaFile,PI,Parent) -> ok end, Parent ! {self(),started}, - ttb_meta_tracer_loop(MetaFile,PI,dict:new()). + case proplists:get_value(overload_check, SessionData) of + {Ms, M, F} -> + catch M:F(init), + erlang:send_after(Ms, self(), overload_check); + _ -> + ok + end, + ttb_meta_tracer_loop(MetaFile,PI,dict:new(),SessionData). -ttb_meta_tracer_loop(MetaFile,PI,Acc) -> +ttb_meta_tracer_loop(MetaFile,PI,Acc,State) -> receive {trace_ts,_,call,{erlang,register,[Name,Pid]},_} -> ttb_store_meta({pid,{Pid,Name}},MetaFile), - ttb_meta_tracer_loop(MetaFile,PI,Acc); + ttb_meta_tracer_loop(MetaFile,PI,Acc,State); {trace_ts,_,call,{global,register_name,[Name,Pid]},_} -> ttb_store_meta({pid,{Pid,{global,Name}}},MetaFile), - ttb_meta_tracer_loop(MetaFile,PI,Acc); + ttb_meta_tracer_loop(MetaFile,PI,Acc,State); {trace_ts,CallingPid,call,{erlang,spawn_opt,[{M,F,Args,_}]},_} -> MFA = {M,F,length(Args)}, NewAcc = dict:update(CallingPid, fun(Old) -> [MFA|Old] end, [MFA], Acc), - ttb_meta_tracer_loop(MetaFile,PI,NewAcc); + ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State); {trace_ts,CallingPid,return_from,{erlang,spawn_opt,_Arity},Ret,_} -> case Ret of {NewPid,_Mref} when is_pid(NewPid) -> ok; @@ -160,14 +173,14 @@ ttb_meta_tracer_loop(MetaFile,PI,Acc) -> T end, Acc), - ttb_meta_tracer_loop(MetaFile,PI,NewAcc); + ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State); {trace_ts,CallingPid,call,{erlang,Spawn,[M,F,Args]},_} when Spawn==spawn;Spawn==spawn_link -> MFA = {M,F,length(Args)}, NewAcc = dict:update(CallingPid, fun(Old) -> [MFA|Old] end, [MFA], Acc), - ttb_meta_tracer_loop(MetaFile,PI,NewAcc); + ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State); {trace_ts,CallingPid,return_from,{erlang,Spawn,_Arity},NewPid,_} when Spawn==spawn;Spawn==spawn_link -> @@ -178,28 +191,53 @@ ttb_meta_tracer_loop(MetaFile,PI,Acc) -> T end, Acc), - ttb_meta_tracer_loop(MetaFile,PI,NewAcc); + ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State); {metadata,Data} when is_list(Data) -> ttb_store_meta(Data,MetaFile), - ttb_meta_tracer_loop(MetaFile,PI,Acc); + ttb_meta_tracer_loop(MetaFile,PI,Acc,State); {metadata,Key,Fun} when is_function(Fun) -> ttb_store_meta([{Key,Fun()}],MetaFile), - ttb_meta_tracer_loop(MetaFile,PI,Acc); + ttb_meta_tracer_loop(MetaFile,PI,Acc,State); {metadata,Key,What} -> ttb_store_meta([{Key,What}],MetaFile), - ttb_meta_tracer_loop(MetaFile,PI,Acc); - - stop when PI=:=true -> - erlang:trace_pattern({erlang,spawn,3},false,[meta]), + ttb_meta_tracer_loop(MetaFile,PI,Acc,State); + overload_check -> + {Ms, M, F} = proplists:get_value(overload_check, State), + case catch M:F(check) of + true -> + erlang:trace(all, false, [all]), + ControlPid = proplists:get_value(ttb_control, State), + ControlPid ! {node_overloaded, node()}, + catch M:F(stop), + ttb_meta_tracer_loop(MetaFile,PI,Acc,lists:keydelete(overload_check, 1, State)); + _ -> + erlang:send_after(Ms, self(), overload_check), + ttb_meta_tracer_loop(MetaFile,PI,Acc, State) + end; + {'DOWN', _, _, _, _} -> + stop_seq_trace(), + self() ! stop, + ttb_meta_tracer_loop(MetaFile,PI,Acc, State); + stop when PI=:=true -> + try_stop_resume(State), + try_stop_overload_check(State), + erlang:trace_pattern({erlang,spawn,3},false,[meta]), erlang:trace_pattern({erlang,spawn_link,3},false,[meta]), erlang:trace_pattern({erlang,spawn_opt,1},false,[meta]), erlang:trace_pattern({erlang,register,2},false,[meta]), erlang:trace_pattern({global,register_name,2},false,[meta]); stop -> - ok + try_stop_resume(State), + try_stop_overload_check(State) + end. + +try_stop_overload_check(State) -> + case proplists:get_value(overload, State) of + undefined -> ok; + {_, M, F} -> catch M:F(stop) end. pnames() -> @@ -224,6 +262,40 @@ pinfo(P,Globals) -> undefined -> [] % the process has terminated end. +autostart_module() -> + element(2, application:get_env(runtime_tools, ttb_autostart_module)). + +try_stop_resume(State) -> + case proplists:get_value(resume, State) of + true -> (autostart_module()):delete_config(); + _ -> ok + end. + +ttb_resume_trace() -> + case (autostart_module()):read_config() of + {error, _} -> + ok; + {ok, Data} -> + Pid = proplists:get_value(ttb_control, Data), + {_, Timeout} = proplists:get_value(resume, Data), + case rpc:call(node(Pid), erlang, whereis, [ttb]) of + Pid -> + Pid ! {noderesumed, node(), self()}, + wait_for_fetch_ready(Timeout); + _ -> + ok + end, + (autostart_module()):delete_config(), + ok + end. + +wait_for_fetch_ready(Timeout) -> + receive + trace_resumed -> + ok + after Timeout -> + ok + end. ttb_store_meta(Data,{local,MetaFile,Port}) when is_list(Data) -> ttb_send_to_port(Port,MetaFile,Data); @@ -275,6 +347,9 @@ ttb_stop(MetaPid) -> %% returns, and then the Port (in {local,MetaFile,Port}) %% cannot be accessed any more. receive {'DOWN', Ref, process, MetaPid, _Info} -> ok end, + stop_seq_trace(). + +stop_seq_trace() -> seq_trace:reset_trace(), seq_trace:set_system_tracer(false). diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src index e6dc7a21d4..095567b165 100644 --- a/lib/runtime_tools/src/runtime_tools.app.src +++ b/lib/runtime_tools/src/runtime_tools.app.src @@ -22,7 +22,8 @@ {modules, [dbg,observer_backend,percept_profile, inviso_rt,inviso_rt_lib,inviso_rt_meta, inviso_as_lib,inviso_autostart,inviso_autostart_server, - runtime_tools,runtime_tools_sup,erts_alloc_config]}, + runtime_tools,runtime_tools_sup,erts_alloc_config, + ttb_autostart]}, {registered, [runtime_tools_sup,inviso_rt,inviso_rt_meta]}, {applications, [kernel, stdlib]}, % {env, [{inviso_autostart_mod,your_own_autostart_module}]}, diff --git a/lib/runtime_tools/src/runtime_tools_sup.erl b/lib/runtime_tools/src/runtime_tools_sup.erl index 1a872c355d..4fcb2292d0 100644 --- a/lib/runtime_tools/src/runtime_tools_sup.erl +++ b/lib/runtime_tools/src/runtime_tools_sup.erl @@ -38,6 +38,8 @@ init(AutoModArgs) -> Flags = {one_for_one, 0, 3600}, Children = [{inviso_rt, {inviso_rt, start_link_auto, [AutoModArgs]}, - temporary, 3000, worker, [inviso_rt]}], + temporary, 3000, worker, [inviso_rt]}, + {ttb_autostart, {ttb_autostart, start_link, []}, + temporary, 3000, worker, [ttb_autostart]}], {ok, {Flags, Children}}. %% ----------------------------------------------------------------------------- -- cgit v1.2.3