diff options
Diffstat (limited to 'lib/runtime_tools/src')
-rw-r--r-- | lib/runtime_tools/src/Makefile | 3 | ||||
-rw-r--r-- | lib/runtime_tools/src/dbg.erl | 65 | ||||
-rw-r--r-- | lib/runtime_tools/src/inviso_rt.erl | 4 | ||||
-rw-r--r-- | lib/runtime_tools/src/inviso_rt_lib.erl | 16 | ||||
-rw-r--r-- | lib/runtime_tools/src/observer_backend.erl | 123 | ||||
-rw-r--r-- | lib/runtime_tools/src/runtime_tools.app.src | 3 | ||||
-rw-r--r-- | lib/runtime_tools/src/runtime_tools_sup.erl | 4 | ||||
-rw-r--r-- | lib/runtime_tools/src/ttb_autostart.erl | 55 |
8 files changed, 211 insertions, 62 deletions
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/dbg.erl b/lib/runtime_tools/src/dbg.erl index 56283f4d3d..385047ee73 100644 --- a/lib/runtime_tools/src/dbg.erl +++ b/lib/runtime_tools/src/dbg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ -export([fun2ms/1]). %% Local exports --export([erlang_trace/3,get_info/0]). +-export([erlang_trace/3,get_info/0,deliver_and_flush/1]). %% Debug exports -export([wrap_presort/2, wrap_sort/2, wrap_postsort/1, wrap_sortfix/2, @@ -348,17 +348,16 @@ trace_port_control(Operation) -> trace_port_control(node(), Operation). trace_port_control(Node, flush) -> - Ref = erlang:trace_delivered(all), - receive - {trace_delivered,all,Ref} -> ok - end, - case trace_port_control(Node, $f, "") of - {ok, [0]} -> - ok; - {ok, _} -> - {error, not_supported_by_trace_driver}; - Other -> - Other + case get_tracer(Node) of + {ok, Port} when is_port(Port) -> + case catch rpc:call(Node,?MODULE,deliver_and_flush,[Port]) of + [0] -> + ok; + _ -> + {error, not_supported_by_trace_driver} + end; + _ -> + {error, no_trace_driver} end; trace_port_control(Node,get_listen_port) -> case trace_port_control(Node,$p, "") of @@ -378,7 +377,14 @@ trace_port_control(Node, Command, Arg) -> {error, no_trace_driver} end. - +%% A bit more than just flush - it also makes sure all trace messages +%% are delivered first, before flushing the driver. +deliver_and_flush(Port) -> + Ref = erlang:trace_delivered(all), + receive + {trace_delivered,all,Ref} -> ok + end, + erlang:port_control(Port, $f, ""). trace_port(file, {Filename, wrap, Tail}) -> @@ -684,18 +690,12 @@ loop({C,T}=SurviveLinks, Table) -> %% tracing on the node it removes from the list of active trace nodes, %% we will call erlang:trace_delivered/1 on ALL nodes that we have %% connections to. - Delivered = fun() -> - Ref = erlang:trace_delivered(all), - receive - {trace_delivered,all,Ref} -> ok - end - end, - catch rpc:multicall(nodes(), erlang, apply, [Delivered,[]]), - Ref = erlang:trace_delivered(all), - receive - {trace_delivered,all,Ref} -> - exit(done) - end; + %% If it is a file trace driver, we will also flush the port. + lists:foreach(fun({Node,{_Relay,Port}}) -> + rpc:call(Node,?MODULE,deliver_and_flush,[Port]) + end, + get()), + exit(done); {From, {link_to, Pid}} -> case (catch link(Pid)) of {'EXIT', Reason} -> @@ -1449,6 +1449,19 @@ new_pattern_table() -> ets:insert(PT, {exception_trace, term_to_binary(x)}), + ets:insert(PT, + {c, + term_to_binary([{'_',[],[{message,{caller}}]}])}), + ets:insert(PT, + {caller_trace, + term_to_binary(c)}), + ets:insert(PT, + {cx, + term_to_binary([{'_',[],[{exception_trace}, + {message,{caller}}]}])}), + ets:insert(PT, + {caller_exception_trace, + term_to_binary(cx)}), PT. diff --git a/lib/runtime_tools/src/inviso_rt.erl b/lib/runtime_tools/src/inviso_rt.erl index ac7ac2a584..b162f5b045 100644 --- a/lib/runtime_tools/src/inviso_rt.erl +++ b/lib/runtime_tools/src/inviso_rt.erl @@ -2359,8 +2359,8 @@ list_wrapset(Prefix,Suffix) -> list_wrapset_2([File|Rest],RegExp) -> Length=length(File), - case regexp:first_match(File,RegExp) of - {match,1,Length} -> % This is a member of the set. + case re:run(File,RegExp) of + {match,[{0,Length}]} -> % This is a member of the set. [File|list_wrapset_2(Rest,RegExp)]; _ -> list_wrapset_2(Rest,RegExp) diff --git a/lib/runtime_tools/src/inviso_rt_lib.erl b/lib/runtime_tools/src/inviso_rt_lib.erl index 2c6964e53e..ee6a72ae0c 100644 --- a/lib/runtime_tools/src/inviso_rt_lib.erl +++ b/lib/runtime_tools/src/inviso_rt_lib.erl @@ -197,15 +197,15 @@ match_modules(RegExpDir,RegExpMod,Actions) -> handle_expand_regexp_2([{Mod,Path}|Rest],RegExpDir,RegExpMod,Result) -> ModStr=atom_to_list(Mod), ModLen=length(ModStr), - case regexp:first_match(ModStr,RegExpMod) of - {match,1,ModLen} -> % Ok, The regexp matches the module. + case re:run(ModStr,RegExpMod) of + {match,[{0,ModLen}]} -> % Ok, The regexp matches the module. if is_list(RegExpDir),is_atom(Path) -> % Preloaded or covercompiled... handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result); is_list(RegExpDir),is_list(Path) -> % Dir reg-exp is used! PathOnly=filename:dirname(Path), % Must remove beam-file name. - case regexp:first_match(PathOnly,RegExpDir) of - {match,_,_} -> % Did find a match, that is enough! + case re:run(PathOnly,RegExpDir,[{capture,none}]) of + match -> % Did find a match, that is enough! handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,[Mod|Result]); _ -> % Either error or nomatch. handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result) @@ -233,8 +233,8 @@ handle_expand_regexp_3([Path|Rest],RegExpDir,RegExpMod,AllLoaded,Result) -> volumerelative -> % Only on Windows!? filename:absname(Path) end, - case regexp:first_match(AbsPath,RegExpDir) of - {match,_,_} -> % Ok, the directory is allowed. + case re:run(AbsPath,RegExpDir,[{capture,none}]) of + match -> % Ok, the directory is allowed. NewResult=handle_expand_regexp_3_1(Path,RegExpMod,AllLoaded,Result), handle_expand_regexp_3(Rest,RegExpDir,RegExpMod,AllLoaded,NewResult); _ -> % This directory does not qualify. @@ -262,8 +262,8 @@ handle_expand_regexp_3_2([File|Rest],RegExpMod,AllLoaded,Result) -> case {lists:keysearch(Mod,1,AllLoaded),lists:member(Mod,Result)} of {false,false} -> % This module is not tried before. ModLen=length(ModStr), - case regexp:first_match(ModStr,RegExpMod) of - {match,1,ModLen} -> % This module satisfies the regexp. + case re:run(ModStr,RegExpMod) of + {match,[{0,ModLen}]} -> % This module satisfies the regexp. handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,[Mod|Result]); _ -> % Error or not perfect match. handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,Result) diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl index 0f428de07a..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 @@ -92,16 +93,22 @@ 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, + 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 @@ -111,13 +118,14 @@ 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}, 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}]}], @@ -130,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; @@ -158,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 -> @@ -176,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() -> @@ -222,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); @@ -273,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). @@ -287,7 +364,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); 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}}. %% ----------------------------------------------------------------------------- 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ń <[email protected]> +%%% 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 <[email protected]> +%%%------------------------------------------------------------------- +-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}. |