diff options
Diffstat (limited to 'lib/runtime_tools/src')
| -rw-r--r-- | lib/runtime_tools/src/Makefile | 6 | ||||
| -rw-r--r-- | lib/runtime_tools/src/dbg.erl | 6 | ||||
| -rw-r--r-- | lib/runtime_tools/src/inviso_as_lib.erl | 155 | ||||
| -rw-r--r-- | lib/runtime_tools/src/inviso_autostart.erl | 201 | ||||
| -rw-r--r-- | lib/runtime_tools/src/inviso_autostart_server.erl | 311 | ||||
| -rw-r--r-- | lib/runtime_tools/src/inviso_rt.erl | 2885 | ||||
| -rw-r--r-- | lib/runtime_tools/src/inviso_rt_lib.erl | 474 | ||||
| -rw-r--r-- | lib/runtime_tools/src/inviso_rt_meta.erl | 1207 | ||||
| -rw-r--r-- | lib/runtime_tools/src/runtime_tools.app.src | 7 | ||||
| -rw-r--r-- | lib/runtime_tools/src/runtime_tools_sup.erl | 12 | 
10 files changed, 8 insertions, 5256 deletions
| diff --git a/lib/runtime_tools/src/Makefile b/lib/runtime_tools/src/Makefile index 810e3e8741..d6750f3a88 100644 --- a/lib/runtime_tools/src/Makefile +++ b/lib/runtime_tools/src/Makefile @@ -36,12 +36,6 @@ RELSYSDIR = $(RELEASE_PATH)/lib/runtime_tools-$(VSN)  MODULES= \  	erts_alloc_config \ -	inviso_rt \ -	inviso_rt_meta \ -	inviso_rt_lib \ -	inviso_as_lib \ -	inviso_autostart \ -	inviso_autostart_server \  	runtime_tools \  	runtime_tools_sup \  	dbg \ diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl index 385047ee73..d35c8e781e 100644 --- a/lib/runtime_tools/src/dbg.erl +++ b/lib/runtime_tools/src/dbg.erl @@ -431,10 +431,8 @@ trace_port1(file, Filename, Options) ->      fun() ->  	    Name = filename:absname(Filename),   	    %% Absname is needed since the driver uses  -	    %% the supplied name without further investigations,  -	    %% and if the name is relative the resulting path  -	    %% might be too long which can cause a bus error -	    %% on vxworks instead of a nice error code return. +	    %% the supplied name without further investigations. +  	    %% Also, the absname must be found inside the fun,  	    %% in case the actual node where the port shall be  	    %% started is on another node (or even another host) diff --git a/lib/runtime_tools/src/inviso_as_lib.erl b/lib/runtime_tools/src/inviso_as_lib.erl deleted file mode 100644 index 75f3d9d004..0000000000 --- a/lib/runtime_tools/src/inviso_as_lib.erl +++ /dev/null @@ -1,155 +0,0 @@ -%% -%% %CopyrightBegin% -%%  -%% Copyright Ericsson AB 2006-2009. 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 -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%%  -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%%  -%% %CopyrightEnd% -%% - -%%% File    : inviso_as_lib.erl -%%% Author  : Lennart �hman <[email protected]> -%%% Description :  -%% The purpose of the inviso autostart library is to provide useful functions -%% for anyone wanting to customize the autostart mechanism in the inviso -%% tracer. It is intended to work well with the example 'inviso_autostart_server'. -%%% -%%% Created : 15 Dec 2005 by Lennart �hman -%% ----------------------------------------------------------------------------- - --module(inviso_as_lib). - --export([setup_autostart/7,setup_autostart/8,setup_autostart/9, -	 inhibit_autostart/1, -	 set_repeat/2,set_repeat_2/2]). -%% ----------------------------------------------------------------------------- - -%% setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,Bindings) = ok|{error,Reason}. -%%    Repeat=integer(), where 0 means no (more) autostarts. -%%    Options=List of options as taken by the runtime component at start-up. -%%    TracerData= Tracerdata as given to inviso_rt:init_tracing. -%%    CmdFiles=[FileName,...] list of string(), files that will be executed -%%      by the subprocess started during autostart. -%%    Bindings=[{VarName,Value},...] Variable bindings for CmdFiles. -%%      VarName=atom(), -%% -%% This function creates the inviso_autostart.config file on Erlang node Node. -%% This is useful when you wish to prepare for an autostarted trace. -setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,Bindings,Translations) -> -    setup_autostart(Node,Repeat,Options,TracerData,CmdFiles, -		    Bindings,Translations,inviso_std_ref,off). -setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,Bindings,Translations,RTtag) -> -    setup_autostart(Node,Repeat,Options,TracerData,CmdFiles, -		    Bindings,Translations,RTtag,off). -setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,Bindings,Translations,RTtag,Dbg) -> -    case rpc:call(Node,inviso_autostart,which_config_file,[]) of -	FileName when is_list(FileName) ->      % Write to this file then. -	    {String,Args}=format_config_file(Repeat,TracerData,Options,CmdFiles, -					     Bindings,Translations,RTtag,Dbg), -	    Bytes=list_to_binary(io_lib:format(String,Args)), -	    case rpc:call(Node,file,write_file,[FileName,Bytes]) of -		ok -> -		    ok; -		{error,Reason} -> -		    {error,{write_file,Reason}}; -		{badrpc,Reason} -> -		    {error,{badrpc,{write_file,Reason}}} -	    end; -	{error,Reason} -> -	    {error,{which_config_file,Reason}}; -	{badrpc,Reason} -> -	    {error,{badrpc,{which_config_file,Reason}}} -    end. -%% ----------------------------------------------------------------------------- - -%% inhibit_autostart(Node) = ok|{error,Reason} -%% -%% Inhibits autostart by simply making the repeat parameter zero in the -%% configuration file at node Node. All other parameters are left untouched. -inhibit_autostart(Node) -> -    set_repeat(Node,0). -%% ----------------------------------------------------------------------------- - -%% set_repeat(Node,N)=ok | {error,Reason} -%%   N=integer(), the number of time autostart shall be allowed. -set_repeat(Node,N) -> -    case examine_config_file(Node) of -	{ok,FileName,Terms} -> -	    NewTerms=[{repeat,N}|lists:keydelete(repeat,1,Terms)], -	    case rpc:call(Node,?MODULE,set_repeat_2,[FileName,NewTerms]) of -		{badrpc,Reason} -> -		    {error,{badrpc,{open,Reason}}}; -		Result -> -		    Result -	    end; -	{error,Reason} -> -	    {error,Reason} -    end. - -%% Must be a sepparate function to do rpc on. The entire function must be done -%% in one rpc call. Otherwise the FD will die since it is linked to the opening -%% process. -set_repeat_2(FileName,NewTerms) -> -    case file:open(FileName,[write]) of -	{ok,FD} -> -	    String=lists:flatten(lists:map(fun(_)->"~w.~n" end,NewTerms)), -	    case catch io:format(FD,String,NewTerms) of -		ok -> -		    file:close(FD), -		    ok; -		{'EXIT',Reason} -> -		    file:close(FD), -		    {error,{format,Reason}} -	    end; -	{error,Reason} -> -	    {error,Reason} -    end. -%% ----------------------------------------------------------------------------- - -examine_config_file(Node) -> -    case rpc:call(Node,inviso_autostart,which_config_file,[]) of -	FileName when is_list(FileName) ->     % Read this file, and then modify it. -	    case rpc:call(Node,file,consult,[FileName]) of -		{ok,Terms} -> -		    {ok,FileName,Terms}; -		{error,Reason} -> -		    {error,{consult,Reason}}; -		{badrpc,Reason} -> -		    {error,{badrpc,{consult,Reason}}} -	    end; -	{error,Reason} -> -	    {error,{which_config_file,Reason}}; -	{badrpc,Reason} -> -	    {error,{badrpc,{which_config_file,Reason}}} -    end. -%% ----------------------------------------------------------------------------- - -format_config_file(Repeat,TracerData,Options,CmdFiles,Bindings,Translations,RTtag,Dbg) -> -    String="~w.~n~w.~n~w.~n~w.~n", -    Args=[{repeat,Repeat}, -	  {mfa,{inviso_autostart_server,init,[[{tracerdata,TracerData}, -					       {cmdfiles,CmdFiles}, -					       {bindings,Bindings}, -					       {translations,Translations}, -					       {debug,Dbg}]]}}, -	  {options,Options}, -	  {tag,RTtag}], -    {String,Args}. -%% ----------------------------------------------------------------------------- - - - - - - - diff --git a/lib/runtime_tools/src/inviso_autostart.erl b/lib/runtime_tools/src/inviso_autostart.erl deleted file mode 100644 index 787292e244..0000000000 --- a/lib/runtime_tools/src/inviso_autostart.erl +++ /dev/null @@ -1,201 +0,0 @@ -%% -%% %CopyrightBegin% -%%  -%% Copyright Ericsson AB 2006-2010. 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 -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%%  -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%%  -%% %CopyrightEnd% -%% -%% Author: Lennart �hman, [email protected] --module(inviso_autostart). - --export([autostart/1,which_config_file/0]). - -%% This module implements the default autostart module for the inviso runtime -%% component. -%% It will: -%% (1) Open the autostart configuration file (either the default or the one -%%     pointed out by the runtime_tools application parameter inviso_autostart_config). -%% (2) Check that the incarnation counter has not reached 0. If so, we do not -%%     allow (yet) one autostart. -%% (3) Rewrite the configuration file if there was an incarnation counter. -%%     (With the counter decreased). -%% (4) Inspect the content of the configuration file and pass paramters in the -%%     return value (which is interpreted by the runtime component). -%% -%% CONTENT OF A CONFIGURATION FILE: -%% A plain text file containing erlang tuple terms, each ended with a period(.). -%% The following parameters are recognized: -%% {repeat,N} N=interger(), -%%   The number of remaining allowed autostart incarnations of inviso. -%% {options,Options} Options=list() -%%   The options which controls the runtime component, such as overload and -%%   dependency. -%% {mfa,{Mod,Func,Args}} Args=list() -%%   Controls how a spy process initiating tracing, patterns and flags shall -%%   be started. -%% {tag,Tag} -%%   The tag identifying the runtime component to control components. -%% ============================================================================= - -%% This function is run in the runtime component's context during autostart -%% to determine whether to continue and if, then how. -autostart(_AutoModArgs) -> -    ConfigFile= -	case application:get_env(inviso_autostart_conf) of -	    {ok,FileName} when is_list(FileName) -> % Use this filename then. -		FileName; -	    {ok,{load,FileNames,{M,F}}} ->  % First load the module, then... -		case try_load_module(FileNames) of -		    ok -> -			autostart_apply(M,F); - -		    false ->                % No such module available -			"inviso_autostart.config" -		end; -	    {ok,{M,F}} ->                   % Use M:F(node()) -		autostart_apply(M,F); -	    {ok,no_autostart} -> -		false; -	    _ ->                            % Use a default name, in CWD! -		"inviso_autostart.config" -	end, -    if -	is_list(ConfigFile) -> -	    case file:consult(ConfigFile) of -		{ok,Terms} ->               % There is a configuration. -		    case handle_repeat(ConfigFile,Terms) of -			ok ->               % Handled or not, we shall continue. -			    {get_mfa(Terms),get_options(Terms),get_tag(Terms)}; -			stop ->             % We are out of allowed starts. -			    true            % Then no autostart. -		    end; -		{error,_} ->                % There is no config file -		    true                    % Then no autostart! -	    end; -	true ->                             % Skip it then. -	    true -    end. - -autostart_apply(M,F) -> -    case catch M:F(node()) of -	FileName when is_list(FileName) -> -	    FileName; -	no_autostart ->                     % No autostart after all. -	    false; -	_ -> -	    "inviso_autostart.config" -    end. - -%% This function is necessary since it is not always the case that all code-paths -%% are set at the time of an autostart. -try_load_module([AbsFileName|Rest]) when is_list(AbsFileName) -> -    case catch code:load_abs(AbsFileName) of % May not be a proper filename. -	{module,_Mod} -> -	    try_load_module(Rest); -	_ -> -	    false -    end; -try_load_module([]) ->                      % Load all beam files successfully. -    ok; -try_load_module(AbsFileName) when is_list(AbsFileName) -> -    try_load_module([AbsFileName]). -%% ----------------------------------------------------------------------------- - -%% Function returning the filename probably used as autostart config file. -%% Note that this function must be executed at the node in question. -which_config_file() -> -    case application:get_env(runtime_tools,inviso_autostart_conf) of -	{ok,FileName} when is_list(FileName) -> % Use this filename then. -	    FileName; -	{ok,{M,F}} ->                       % Use M:F(node()) -	    case catch M:F(node()) of -		FileName when is_list(FileName) -> -		    FileName; -		_ -> -		    {ok,CWD}=file:get_cwd(), -		    filename:join(CWD,"inviso_autostart.config") -	    end; -	_ ->                                % Use a default name, in CWD! -	    {ok,CWD}=file:get_cwd(), -	    filename:join(CWD,"inviso_autostart.config") -    end. -%% ----------------------------------------------------------------------------- - - -%% Help function which finds out if there is a limit on the number of times -%% we shall autostart. If there is a repeat parameter and it is greater than -%% zero, the file must be rewritten with the parameter decreased with one. -%% Returns 'ok' or 'stop'. -handle_repeat(FileName,Terms) -> -    case lists:keysearch(repeat,1,Terms) of -	{value,{_,N}} when N>0 ->           % Controlls how many time more. -	    handle_repeat_rewritefile(FileName,Terms,N-1), -	    ok;                             % Indicate that we shall continue. -	{value,_} ->                        % No we have reached the limit. -	    stop; -	false ->                            % There is no repeat parameter. -	    ok                              % No restrictions then! -    end. - -%% Help function which writes the configuration file again, but with the -%% repeat parameter set to NewN. -%% Returns nothing significant. -handle_repeat_rewritefile(FileName,Term,NewN) -> -    case file:open(FileName,[write]) of -	{ok,FD} -> -	    NewTerm=lists:keyreplace(repeat,1,Term,{repeat,NewN}), -	    handle_repeat_rewritefile_2(FD,NewTerm), -	    file:close(FD); -	{error,_Reason} ->                  % Not much we can do then?! -	    error -    end. - -handle_repeat_rewritefile_2(FD,[Tuple|Rest]) -> -    io:format(FD,"~w.~n",[Tuple]), -    handle_repeat_rewritefile_2(FD,Rest); -handle_repeat_rewritefile_2(_,[]) -> -    true. -%% ----------------------------------------------------------------------------- - -%% Three help functions finding the parameters possible to give to the runtime -%% component. Note that some of them have default values, should the parameter -%% not exist. -get_mfa(Terms) -> -    case lists:keysearch(mfa,1,Terms) of -	{value,{_,MFA}} -> -	    MFA; -	false -> -	    false -    end. - -get_options(Terms) -> -    case lists:keysearch(options,1,Terms) of -	{value,{_,Options}} -> -	    Options; -	false -> -	    [] -    end. - -get_tag(Terms) -> -    case lists:keysearch(tag,1,Terms) of -	{value,{_,Tag}} -> -	    Tag; -	false -> -	    default_tag -    end. -%% ----------------------------------------------------------------------------- - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - diff --git a/lib/runtime_tools/src/inviso_autostart_server.erl b/lib/runtime_tools/src/inviso_autostart_server.erl deleted file mode 100644 index 1e352822f4..0000000000 --- a/lib/runtime_tools/src/inviso_autostart_server.erl +++ /dev/null @@ -1,311 +0,0 @@ -%% -%% %CopyrightBegin% -%%  -%% Copyright Ericsson AB 2006-2010. 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 -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%%  -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%%  -%% %CopyrightEnd% -%% -%% Author: Lennart �hman, [email protected] -%% --module(inviso_autostart_server). --export([init/1]). - -%% ----------------------------------------------------------------------------- -%% Internal exports -%% ----------------------------------------------------------------------------- --export([cmd_file_interpreter_init/4]). -%% ----------------------------------------------------------------------------- - - -%% This module provides a (well working) example of how to program an -%% autostart server responsible for initializing trace, setting patterns -%% and flags. -%% -%% The general idea is that this code spawns interpreter processes in order to -%% execute commands concurrently. Each of the interpreter processes opens one or -%% several files (in sequence) containing erlang function calls which are evaluated -%% in the interpreter process context. -%% The argument provided to init shall be a list of options controlling -%% how to initialize tracing, which file(s) to open and variable bindings. -%% -%% This autostart_server interpreters understands standard inviso trace case files. -%% -%% The runtime component provides an API very similar to the API provided -%% by the control component. It is therefore easy to translate inviso calls to -%% inviso_rt calls. -%% -%% This process may be killed by the inviso_rt process if stop_tracing is called. -%% The reason is that there is no time limit to the interpreter processes. Hence -%% they should be killed if tracing is not possible anylonger. -%% ============================================================================= - - -%% ----------------------------------------------------------------------------- - -%% The independent autostart process spawned by the runtime component to carry -%% out initializations is spawened on this function (if using the example -%% autostart which comes with inviso). -%% ArgsFromConfig is as can be heard from the name comming from a paramater in -%% the autostart configuration file. Here it is supposed to be: -%%   ArgsFromConfig=[ServerParam,...] -%%     ServerParam={tracerdata,TracerData}|{cmdfiles,Files}|{bindings,Bindings}| -%%       {translations,Translations}|{debug,DbgLevel} -%%       TracerData=tracerdata given to inviso_rt:init_tracing/1 function. -%%       Files=[FileNameSpecs,...] where each FileNameSpecs will be executed in -%%         a separate process. Making each FileNameSpec parallel. -%%         FileNameSpecs=[FileNameSpec,...] -%%           FileNameSpec=FileName | {FileName,Bindings} -%%       Bindings=[{Var,Value},...] variable environment understood by -%%         erl_eval:exprs/2. -%%       Translations=[Translation,...] -%%       A translation file is a text-file with following tuples -%%         Translation={{Mod,Func,Arity,{Mod2,Func2,ParamMF}}}| -%%                     {{Func,Arity,{Mod2,Func2,ParamMF}}} -%%           ParamMF={M,F} | any() -%%           Translates Mod:Func/Arity to Mod2:Func2 with the arguments to -%%             Mod:Func translated using M:F/1. Note that ParamMF is not -%%             necessarily an MF. If no translation shall be done, ParamMF -%%             shall be anything else but an MF. -%%           Also note that Mod is optional in a Translation. That means that -%%           function calls without a module in the trace case file will -%%           be translated according to that translation. -init(ArgsFromConfig) -> -    case get_tracerdata_opts(ArgsFromConfig) of -	{ok,TracerData} ->                  % Otherwise we can not start a trace! -	    case inviso_rt:init_tracing(TracerData) of -		{ok,_Response} ->                   % Ok, tracing has been initiated. -		    case get_cmdfiles_opts(ArgsFromConfig) of -			{ok,CmdFiles} ->    % List of cmd-files. -			    Bindings=get_initialbindings_opts(ArgsFromConfig), -			    Translations=get_translations_opts(ArgsFromConfig), -			    Dbg=get_dbg_opts(ArgsFromConfig), -			    Procs=start_cmd_file_interpreters(CmdFiles, -							      Bindings, -							      Translations, -							      Dbg), -			    loop(Procs,Dbg); % Wait for procs to be done. -			false ->            % Then we can terminate normally. -			    true -		    end; -		{error,Reason} ->           % This is fault, lets terminate abnormally. -		    exit({inviso,{error,Reason}}) -	    end; -	false ->                            % Then there is not much use then. -	    true                            % Just terminate normally. -    end. -%% ----------------------------------------------------------------------------- - -%% Help function which starts a process for each item found in the FileNames -%% list. The idea is that each item will be processed concurrently. The items -%% them selves may be a sequence of filenames. -%% Returns a list of spawned interpret processes. -start_cmd_file_interpreters([FileNames|Rest],Bindings,Translations,Dbg) -> -    P=spawn_link(?MODULE,cmd_file_interpreter_init,[FileNames,Bindings,Translations,Dbg]), -    MRef=erlang:monitor(process,P),         % Can't trap exits in this process. -    [{P,MRef}|start_cmd_file_interpreters(Rest,Bindings,Translations,Dbg)]; -start_cmd_file_interpreters([],_,_,_) -> -    []. -%% ----------------------------------------------------------------------------- - - -%% The loop where this process simply waits for all of the interpreters to be -%% done. Note that that may take som time. An interpreter may take as long time -%% necessary to do its task. -loop(Procs,Dbg) -> -    receive -	{'DOWN',MRef,process,Pid,_Reason} -> -	    case lists:keysearch(MRef,1,Procs) of -		{value,{Pid,_}} ->          % It was an interpreter that terminated. -		    case lists:keydelete(MRef,1,Procs) of -			[] ->               % No more interpreters. -			    true;           % Then terminate. -			NewProcs -> -			    loop(NewProcs,Dbg) -		    end; -		false -> -		    loop(Procs,Dbg) -	    end; -	_ -> -	    loop(Procs,Dbg) -    end. - - -%% ----------------------------------------------------------------------------- -%% The interpret process. -%% -%% An interpreter process executes trace case files. Several interpreter processes -%% may be running in parallel. It is not within the scoop of this implementation -%% of an autostart server to solve conflicts. (You may implement your own autostart -%% server!). -%% An interpret process may run for as long as necessary. Hence the function called -%% within the trace case file can contain wait functions, waiting for a certain -%% system state to occure before continuing. -%% Note that this process also mixes global and local bindings. GlobalBindings -%% is a binding() structure, where LocalBindings is a list of {Var,Value}. -%% Further it is possible to let FileName be a {inviso,Func,Args} tuple instead. -%% ----------------------------------------------------------------------------- - -%% Init function for an interpreter process instance. -cmd_file_interpreter_init(FileNames,GlobalBindings,Translations,Dbg) -> -    interpret_cmd_files(FileNames,GlobalBindings,Translations,Dbg). - -interpret_cmd_files([{FileName,LocalBindings}|Rest],GlobalBindings,Translations,Dbg) -> -    Bindings=join_local_and_global_vars(LocalBindings,GlobalBindings), -    interpret_cmd_files_1(FileName,Bindings,Translations,Dbg), -    interpret_cmd_files(Rest,GlobalBindings,Translations,Dbg); -interpret_cmd_files([],_,_,_) ->            % Done, return nothing significant! -    true; -interpret_cmd_files(FileName,GlobalBindings,Translations,Dbg) -> -    interpret_cmd_files_1(FileName,GlobalBindings,Translations,Dbg). -%    interpret_cmd_files(Rest,GlobalBindings,Translations,Dbg). - -%% This is "inline" inviso calls. -interpret_cmd_files_1({inviso,F,Args},Bindings,Translations,Dbg) -> -    {ok,Tokens1,_}=erl_scan:string("inviso:"++atom_to_list(F)++"("), -    Tokens2=tokenize_args(Args), -    {ok,Tokens3,_}=erl_scan:string(")."), -    case erl_parse:parse_exprs(Tokens1++Tokens2++Tokens3) of -	{ok,Exprs} -> -	    interpret_cmd_files_3(Bindings,Exprs,Translations,Dbg); -	{error,_Reason} -> -	    error -    end; -interpret_cmd_files_1({Mod,Func,Args},_Bindings,_Translations,_Dbg) -> -    catch apply(Mod,Func,Args); -%% This is the case when it actually is a trace case file. -interpret_cmd_files_1(FileName,Bindings,Translations,Dbg) -> -    case file:open(FileName,[read]) of -	{ok,FD} -> -	    interpret_cmd_files_2(FD,Bindings,io:parse_erl_exprs(FD,""),Translations,Dbg), -	    file:close(FD); -	{error,Reason} ->                   % Something wrong with the file. -	    inviso_rt_lib:debug(Dbg,interpret_cmd_files,[FileName,{error,Reason}]) -    end. - -%% Help function which handles Exprs returned from io:parse_erl_exprs and -%% tries to eval them. It is the side-effects we are interested in, like -%% setting flags and patterns. Note that we will get a failure should there -%% be a variable conflict. -%% Also note that there is logic to translate control component API calls to -%% corresponding runtime component calls. -%% Returns nothing significant. -interpret_cmd_files_2(FD,Bindings,{ok,Exprs,_},Translations,Dbg) -> -    {next,NewBindings}=interpret_cmd_files_3(Bindings,Exprs,Translations,Dbg), -    interpret_cmd_files_2(FD,NewBindings,io:parse_erl_exprs(FD,""),Translations,Dbg); -interpret_cmd_files_2(FD,Bindings,{error,ErrorInfo,Line},Translations,Dbg) -> -    inviso_rt_lib:debug(Dbg,parse_erl_exprs,[ErrorInfo,Line]), -    interpret_cmd_files_2(FD,Bindings,io:parse_erl_exprs(FD,""),Translations,Dbg); -interpret_cmd_files_2(_,_,{eof,_},_,_) ->    % End of file. -    true. - -interpret_cmd_files_3(Bindings,Exprs,Translations,Dbg) -> -    case catch inviso_rt_lib:transform(Exprs,Translations) of -	NewExprs when is_list(NewExprs) ->     % We may have translated the API. -	    case catch erl_eval:exprs(NewExprs,Bindings) of -		{'EXIT',Reason} -> -		    inviso_rt_lib:debug(Dbg,exprs,[Exprs,Bindings,{'EXIT',Reason}]), -		    {next,Bindings}; -		{value,_Val,NewBindings} -> % Only interested in the side effects! -		    {next,NewBindings} -	    end; -	{'EXIT',Reason} -> -	    inviso_rt_lib:debug(Dbg,translate2runtime_funcs,[Exprs,Reason]), -	    {next,Bindings} -    end. - -%% Help function adding variables to a bindings structure. If the variable already -%% is assigned in the structure, it will be overridden. Returns a new -%% bindings structure. -join_local_and_global_vars([{Var,Val}|Rest],Bindings) when is_atom(Var) -> -    join_local_and_global_vars(Rest,erl_eval:add_binding(Var,Val,Bindings)); -join_local_and_global_vars([_|Rest],Bindings) -> -    join_local_and_global_vars(Rest,Bindings); -join_local_and_global_vars([],Bindings) -> -    Bindings. - -%% Help function returning a string of tokens, including "," separation -%% between the arguments. -tokenize_args(Args=[Arg|Rest]) when length(Args)>1 -> -    AbsTerm=erl_parse:abstract(Arg), -    Tokens=erl_parse:tokens(AbsTerm), -    {ok,Token,_}=erl_scan:string(","), -    Tokens++Token++tokenize_args(Rest); -tokenize_args([Arg]) -> -    AbsTerm=erl_parse:abstract(Arg), -    erl_parse:tokens(AbsTerm); -tokenize_args([]) -> -    "". -%% ----------------------------------------------------------------------------- - - -%% ----------------------------------------------------------------------------- -%% Help functions working on the options given as argument to init during spawn. -%% ----------------------------------------------------------------------------- - -get_tracerdata_opts(ArgsFromConfig) -> -    case lists:keysearch(tracerdata,1,ArgsFromConfig) of -	{value,{_,{mfa,{M,F,CompleteTDGargs}}}} -> % Dynamic tracerdata. -	    case catch apply(M,F,CompleteTDGargs) of -		{'EXIT',_Reason} -> -		    false; -		TracerData -> -		    {ok,TracerData} -	    end; -	{value,{_,TracerData}} ->           % Interpret this as static tracerdata. -	    {ok,TracerData}; -	false -> -	    false -    end. -%% ----------------------------------------------------------------------------- - -get_cmdfiles_opts(ArgsFromConfig) -> -    case lists:keysearch(cmdfiles,1,ArgsFromConfig) of -	{value,{_,CmdFiles}} -> -	    {ok,CmdFiles}; -	false -> -	    false -    end. -%% ----------------------------------------------------------------------------- - -get_initialbindings_opts(ArgsFromConfig) -> -    case lists:keysearch(bindings,1,ArgsFromConfig) of -	{value,{_,Bindings}} -> -	    Bindings; -	false ->                            % Then we use empty bindings. -	    erl_eval:new_bindings() -    end. -%% ----------------------------------------------------------------------------- - -get_translations_opts(ArgsFromConfig) -> -    case lists:keysearch(translations,1,ArgsFromConfig) of -	{value,{_,Translations}} -> -	    Translations; -	false ->                            % This becomes nearly point less. -	    [] -    end. -%% ----------------------------------------------------------------------------- - -get_dbg_opts(ArgsFromConfig) -> -    case lists:keysearch(debug,1,ArgsFromConfig) of -	{value,{_,DbgLevel}} -> -	    DbgLevel; -	false -> -	    off -    end. -%% ----------------------------------------------------------------------------- - -%% EOF - - - diff --git a/lib/runtime_tools/src/inviso_rt.erl b/lib/runtime_tools/src/inviso_rt.erl deleted file mode 100644 index b162f5b045..0000000000 --- a/lib/runtime_tools/src/inviso_rt.erl +++ /dev/null @@ -1,2885 +0,0 @@ -%% -%% %CopyrightBegin% -%%  -%% Copyright Ericsson AB 2005-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 -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%%  -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%%  -%% %CopyrightEnd% -%% -%% Description: -%% The runtime component of the trace tool Inviso. -%% -%% Authors: -%% Ann-Marie L�f, [email protected] -%% Lennart �hman, [email protected] -%% ----------------------------------------------------------------------------- - --module(inviso_rt). - - -%% ----------------------------------------------------------------------------- -%% interface for supervisor -%% ----------------------------------------------------------------------------- --export([start_link_man/3,start_link_auto/1]). - -%% API for controll component. --export([start/4,stop/1,  -	 init_tracing/2,stop_tracing_parallel/1, -	 try_to_adopt/3,confirm_connection/2,get_node_info/1, -	 suspend/2,call_suspend/2,cancel_suspension/1,change_options/2, -	 clear/2,clear_all_tp/1, -	 flush/1, -	 trace_patterns_parallel/3, -	 trace_flags_parallel/3,trace_flags_parallel/2,trace_flags_parallel/1, -	 meta_tracer_call_parallel/2, -	 get_status/1,get_tracerdata/1,list_logs/1,list_logs/2,fetch_log/2,fetch_log/3, -	 delete_log/1,delete_log/2, -	 state/1]). -%% ----------------------------------------------------------------------------- - -%% API mostly for autostart scripts, instead of corresponding control component -%% apis not available doing local function calls. --export([init_tracing/1,tp/4,tp/5,tp/1,tpg/4,tpg/5,tpg/1, -	 tpl/4,tpl/5,tpl/1, -	 ctp/1,ctp/3,ctpg/1,ctpg/3,ctpl/1,ctpl/3, -	 init_tpm/4,init_tpm/7, -	 tpm/4,tpm/5,tpm/8,tpm_tracer/4,tpm_tracer/5,tpm_tracer/8, -	 tpm_ms/5,tpm_ms_tracer/5, -	 ctpm_ms/4, -	 local_register/0,global_register/0, -	 ctpm/3,remove_local_register/0,remove_global_register/0, -	 tf/2,tf/1,ctf/2,ctf/1]). -%% ----------------------------------------------------------------------------- - -%% Internal exports. --export([init/4,auto_init/2,fetch_init/4]). -%% ----------------------------------------------------------------------------- - -%% ----------------------------------------------------------------------------- -%% Constants. -%% ----------------------------------------------------------------------------- - --define(DEFAULT_OVERLOAD_FUNC,default_overload_func). --define(NO_LOADCHECK,no_loadcheck). - --define(RT_SUP,runtime_tools_sup).          % Refers to the registered name. --define(CTRL,inviso_c).                     % Refers to the registered name. -%% ----------------------------------------------------------------------------- - -%% ----------------------------------------------------------------------------- -%% Record definition. -%% ----------------------------------------------------------------------------- - -%% #rt -%% All record fields must be bound to listed values when leaving init or  -%% auto_init. -%% dependency: Timeout accepting being without control component. -%% overload : Controlls which module to call, if any, when time for a check. -%% timer_ref: Used when timing delayed shutdown due to lost control component. --record(rt,{state = new,            % new | idle | tracing -	    status = running,       % running | {suspended, Reason} -	    next_loadcheck = now(), % now | "No Loadcheck" -	    parent,                 % pid() -	    tracerdata,             % undefined|{fun(),term()}|{file,Param}|{ip,Param} -	    tracer_port,            % port() | undefined -	    handler,                % {fun(), term()} | undefined -	    auto_starter,           % pid() | undefined; proc starting interpreters. -	    meta_tracer,            % undefined | pid() -	    fetchers=[],            % [pid(),...] processes transfering logfiles. -%	     spies = [], -	    dependency={infinity,node()}, % {TOut,Node} | TOut; TOut=int()|infinity -	    overload=no_loadcheck,  % ?NO_LOADCHECK|{LoadMF,Interval,InitMFA,RemoveMFA} -	    overload_data=void,     % Datastructure given to LoadMF and RemoveMFA. -	    timer_ref,              % undefined | reference() -	    ctrl,                   % undefined | pid() -	    ctrl_ref,               % undefined | reference() -	    vsn,                    % list() -	    tag                     % term() -	   }). -%% ----------------------------------------------------------------------------- - - -%% ============================================================================== -%% Start API -%% ============================================================================== - -%% Note that the runtime component may be started in many different ways. -%% It can be autostarted by the runtime_tools_sup during initial start-up of the -%% system. It is actually most likely that it will be started that way. However -%% if there are no autostart trace-cases to run, the inviso_rt runtime component -%% will terminate. It will then however remain as a child of the runtime_tools_sup -%% supervisor. This means that if the runtime component is started again, manually, -%% by the control component, some actions must be taken. -%% For instance is it very likely that the child already exists. But since it -%% must be started with different arguments when started manually, the child-spec -%% must be changed. -%% -%% The runtime component is not a proper gen_server, to allow full control of -%% what happens. It however mimcs gen_server behaviour to be managed by the -%% runtime_tools_sup supervisor. - - -%% start_link_auto(AutoModArgs)={ok,Pid} -%% -%% This function is entered into the child-spec when planning on doing autostart -%% of the runtime component. The autostart is controlled by the so called -%% inviso_autostart_mod. It is an application environment parameter of the -%% runtime_tools application. If it exists, it shall point out a module name. -%% If it does not exist, the default 'inviso_autostart' module will be tried. -%% Note that these start_link functions do not implement proper otp-behaviour. -%% For instance they return {ok,Pid} immediately making the init-phase of the -%% runtime component process empty. -%% -%% The inviso_autostart_mod shall export one function: -%%   autostart(AutoModArgs) -> {MFA,Options,Tag}, where -%%     AutoModArgs=term(), comes from the application start parameters in the -%%       runtime_tools application resource file. -%%     MFA={Mod,Func,Args} | term(). -%%       If it is MFA it will cause a trace initiator process to start spawning -%%       on spawn_link(Mod,Func,Args). The trace initiator may for instance -%%       initiate the wanted tracing. -start_link_auto(AutoModArgs) -> -    {ok,spawn_link(?MODULE,auto_init,[AutoModArgs,self()])}. -%% ------------------------------------------------------------------------------ - -%% This function is entered into the child-specification of the runtime_tools_sup -%% if the runtime component shall be started manually via the control component. -start_link_man(Ctrl,Options,Tag) -> -    {ok,spawn_link(?MODULE,init,[Ctrl,Options,Tag,self()])}. -%% ------------------------------------------------------------------------------ - -%% start(Node,Options,Tag,Condition)=tbd -%%   Node=The node where the runtime component shall be started. -%%   Options=[Opt]; List of options to the runtime component. -%%     Opt={dependency,Val}|{dependency,{Val,Node}} -%%       Val=int()|infinity -%%       If the runtime component may run on its own or not. Val=0 means a runtime -%%       component which will terminate immediately without its control component. -%%       Note that if the runtime component is started manually, the Node part -%%       is never used. The runtime is supposed to be dependent of the Ctrl mentioned -%%       in the start_link_man parameters. -%%     Opt={overload,OverLoad} | overload -%%           The latter means no loadcheck. Necessary if changing the options. -%%       Overload=Iterval (int() in milliseconds) | -%%         {LoadMF,Interval}|{LoadMF,Interval,InitMFA,RemoveMFA} -%%           LoadMF={Mod,Func}|function() -%%           InitMFA,RemoveMFA={Mod,Func,ArgList} where -%%             apply(InitM,InitF,InitArgs) -> {ok,DataStruct}|'void'. -%%             apply(RemoveM,RemoveF,[DataStruct|Args]) -> don't care -%%             LoadMF is called each time loadcheck is performed. -%%               Mod:Func(DataStruct)->ok|{suspend,Reason} -%%           If just Interval is used, it means using a default overload check. -%%     Tag=term(), used to identify an incarnation of a runtime component so that -%%       a control component reconnecting will know if it was its own incarnation -%%       still alive, or some elses. -%%     Condition='if_ref'|term(). Controls if we want to adopt the runtime component. -%%       If 'if_ref' is stated it means that we only want to adopt a runtime component -%%       with the suggested Tag. -%% -%% This is the API used by the control component when tries to start a runtime -%% component. Note that it will try to adopt an already running, if possible. -%% Adoptions are only possible if the runtime component at hand is running -%% without control component. -start(Node, Options, Tag, Condition) when Node == node() -> -    ChildSpec = {?MODULE, {?MODULE, start_link_man, [self(), Options, Tag]},  -		 temporary, 5000, worker, [?MODULE]}, -    case catch supervisor:start_child(?RT_SUP, ChildSpec) of -	{ok, Pid} when is_pid(Pid) -> -	    {node_info, _Node, Pid, VSN, State, Status, _Tag} =  -		get_node_info(Pid), -	    {node_info, Node, Pid, VSN, State, Status, new}; -	{error, already_present} -> -	    supervisor:delete_child(?RT_SUP, ?MODULE), -	    start(Node, Options, Tag, Condition); -	{error, {already_started, Pid}} -> -	    try_to_adopt(Pid, Tag, Condition); -	{error,Reason} -> -	    {error,Reason}; -	{'EXIT',Reason} -> -	    {error,Reason} -    end; -start(Node, Options, Tag, Condition) -> -    case rt_version(Node) of -	{error,Error} -> -	    {error,Error}; -	_VSN -> -	    ChildSpec = {?MODULE, {?MODULE, start_link_man,  -				    [self(), Options, Tag]},  -			 temporary, 5000, worker, [?MODULE]}, -	    case catch rpc:call(Node, supervisor, start_child,  -				[?RT_SUP, ChildSpec]) of -		{ok, Pid} when is_pid(Pid) -> -		    {node_info, _Node, Pid,  -		     VSN, State, Status, _Tag} = get_node_info(Pid), -		    {node_info, Node, Pid, VSN, State, Status, new}; -		{error, already_present} -> -		    rpc:call(Node, supervisor, delete_child,  -			     [?RT_SUP, ?MODULE]), -		    start(Node, Options, Tag, Condition); -		{error, {already_started, Pid}} -> -		    try_to_adopt(Pid, Tag, Condition); -		{error,Reason} ->           % Could not start child. -		    {error,Reason}; -		{badrpc,nodedown} -> -		    {error,nodedown}; -		{badrpc,Reason} -> -		    {error,{badrpc,Reason}}; -		{'EXIT',Reason} -> -		    {error,Reason} -	    end -    end. - -rt_version(Node) -> -    case catch rpc:call(Node,application,loaded_applications,[]) of -	List when is_list(List) -> -	    case lists:keysearch(runtime_tools,1,List) of -		{value,{_,_,VSN}} -> -		    VSN; -		false -> -		    {error,not_loaded} -	    end; -	{badrpc,nodedown} -> -	    {error,nodedown}; -	{'EXIT',Reason} ->  -	    {error,Reason} -    end. -%% ------------------------------------------------------------------------------ - -%% stop(Node)=ok|{error,Reason} -%% Stops the runtim component on node Node. Note that this is mearly calling the -%% supervisor API to shutdown the inviso_rt child belonging to the runtime_tools_sup. -stop(Node) when Node==node() -> -    supervisor:terminate_child(?RT_SUP,?MODULE), -    supervisor:delete_child(?RT_SUP,?MODULE), -    ok; -stop(Node) -> -    case catch rpc:call(Node,supervisor,terminate_child,[?RT_SUP,?MODULE]) of -	ok -> -	    stop_delete_child(Node); -	{error,_} ->                        % No child running. -	    stop_delete_child(Node);        % Make sure we remove it also. -	{badrpc,Reason} -> -	    {error,{badrpc,Reason}}; -	{'EXIT',Reason} -> -	    {error,Reason} -    end. - -stop_delete_child(Node) -> -    case catch rpc:call(Node,supervisor,delete_child,[?RT_SUP,?MODULE]) of -	ok -> -	    ok; -	{error,_} ->                        % No child running. -	    ok; -	{badrpc,Reason} -> -	    {error,{badrpc,Reason}}; -	{'EXIT',Reason} -> -	    {error,Reason} -    end. -%% ------------------------------------------------------------------------------ - - -%% ============================================================================== -%% API for the control component. -%% ============================================================================== - -%% init_tracing(TracerData) -> -%%   TracerData = LogTD | [{trace,LogTD},{ti,TiTD}] -%%   LogTD = {HandlerFun, Data} | collector | -%%           {relayer, pid()} | {ip, IPPortParameters} | -%%           {file, FilePortParameters} -%%   TiTD = {file,FileName} | {file,FileName,{InitPublLD,RemovePublLD,CleanPublLD}} -%%          | {relay,Node} | {relay,Node,{InitPublLD,RemovePublLD,CleanPublLD}} -%%   HandlerFun=fun(TraceMsg,Data)->NewData -%%   IPPortParameters = Portno | {Portno, Qsiz} -%%   Qsiz =  -%%   FilePortParameters = {Filename, wrap, Tail, {time, WrapTime}, WrapCnt} | -%%                        {FileName, wrap, Tail, WrapSize, WrapCnt} | -%%                        {FileName, wrap, Tail, WrapSize} | -%%                        {FileName, wrap, Tail} | FileName -%% Defines a tracer: -%% {HandlerFun, Data} - will be used as handler inside the runtime component for -%%   every incomming trace message. -%% relayer - the runtime component will relay all comming trace messages to -%%   the runtime component Pid. -%% collector - the runtime component is used as tracer or collector of relayed  -%%             trace messages using the default handler writing them to io. -%% ip | file - will start a tracer port using PortParameters -init_tracing(Pid,TracerData) -> -    call(Pid,{init_tracing,TracerData}). -%% ------------------------------------------------------------------------------ - -%% stop_tracing(RTpids)=[{Node,NodeResult},...] -%%   RTpids=[RTinfo,...] -%%     RTinfo={RTpid,Node} | {{error,Reason},Node} -%%   NodeResult={ok,State} | {error,Reason} -%% Sends a request to stop tracing to all nodes in RTpids, in parallel. Stop -%% tracing means that all trace flags are removed and the nodes go to idle -%% state. -stop_tracing_parallel(RTpids) -> -    call_parallel(lists:map(fun({Pid,Node})->{Pid,Node,stop_tracing}; -			       (Error)->Error -			    end, -			    RTpids)). -%% ------------------------------------------------------------------------------ - -%% try_to_adopt(Pid,NewTag,Condition)= -%%   {node_info,node(),self(),VSN,State,Status,{tag,PreviousTag}}|{error,Reason} -%%   NewTag=term(), the identification tag we want the runtime component to use -%%     from now on if adoption was successful. -%%   Condition='if_ref', only adopt if current tag is NewTag. -%%   PreviousTag= the tag the runtime component had before it accepted the -%%     adoption. -%% This function shall only be used by a control component wishing to adopt this -%% runtime component. -try_to_adopt(Pid, Tag, Condition) -> -    call(Pid,{try_to_adopt,Tag,Condition}). -%% ------------------------------------------------------------------------------ - -%% confirm_connection(Pid,Tag)= {node_info,node(),self(),VSN,State,Status,Tag}| -%%   {error,refused}. -%% Must only be used by a control component having been contacted by the runtime -%% component Pid. It confirms to the runtime component that the control component -%% has accepted the connect request. -confirm_connection(Pid,Tag) -> -    call(Pid,{confirm_connection,Tag}). -%% ------------------------------------------------------------------------------ - -%% get_node_info(Pid)={node_info,Node,Pid,VSN,State,Status,Tag}. -get_node_info(Pid) -> -    call(Pid,get_node_info). -%% ------------------------------------------------------------------------------ - -%% suspend(NodeOrPid,Reason)=ok -%% call_suspend(NodeOrPid,Reason)=ok -%% Makes the runtime component and all of its helpers suspend. suspend/2 is -%% assynchronous. -suspend(NodeOrPid,Reason) -> -    cast(NodeOrPid,{suspend,Reason}). - -call_suspend(NodeOrPid,Reason) -> -    call(NodeOrPid,{suspend,Reason}). -%% ------------------------------------------------------------------------------ - -%% cancel_suspension(Pid)=ok -%% Function moving the runtime component to status running. Regardless of its -%% current status. -cancel_suspension(Pid) -> -    call(Pid,cancel_suspension). -%% ------------------------------------------------------------------------------ - -%% change_options(Pid,Options)=ok -%%   Options=list(); see the start_link_XXX functions. -%% Changes options according to Options list. -%% Changing the control component we shall be depending on has no effect. The -%% dependency value in self can however be changed, and takes effect immediately. -change_options(Pid,Options) -> -    call(Pid,{change_options,Options}). -%% ------------------------------------------------------------------------------ - -%% clear_all_tp(Pid)=ok -%% Function removing all, both local and global trace-patterns from the node. -clear_all_tp(Pid) -> -    call(Pid,clear_all_tp). -%% ------------------------------------------------------------------------------ - -%% clear(Pid,Options)={ok,{new,Status}} -%%   Options=[Opt,...] -%%     Opt=keep_trace_patterns | keep_log_files -%% Resets the runtime component to state 'new' by stopping all ongoing tracing, -%% closing and removing all associated logfiles. The Options can be used to -%% prevent the runtime component from being totally erased. -clear(Pid,Options) -> -    call(Pid,{clear,Options}). -%% ------------------------------------------------------------------------------ - -%% flush(Pid)=ok | {error,Reason} -%% Sends the flush command to the trace-port, if we are using a trace-port and -%% are tracing. -flush(Pid) -> -    call(Pid,flush). -%% ------------------------------------------------------------------------------ - -%% trace_patterns_parallel(RTpids,Args,Flags)=[{Node,Answer},...] -%%   RTpids=[{RTpid,Node},...] or [{Error,Node},...] -%%   Args=[Arg,...] -%%     Arg={Mod,Func,Arity,MS}|{Mod,Func,Arity,MS,Opts} -%%       Mod=atom()|reg_exp()|{Dir,reg_exp()} -%%         Dir=reg_exp() -%%   Answer=[Answer,...] -%%     Answer=int()|{error,Reason} -%% API function for the control component sending trace-patterns to a list of -%% runtime components. Returns a [{Node,Answer},...] list in the same order. -trace_patterns_parallel(RTpids,Args,Flags) ->     % Same args and flags for all. -    call_parallel(lists:map(fun({Pid,Node})when is_pid(Pid)->{Pid,Node,{tp,Args,Flags}}; -			       (Error)-> Error -			    end, -			    RTpids)). -%% ------------------------------------------------------------------------------ - -%% trace_flags_parallel(RTpids,Args,How)= -%% trace_flags_parallel(RTpidsArgs,How)= -%% trace_flags_parallel(RTpidsArgsHow)=[{Node,Reply},...] -%%   RTpids=[RTpidEntry,...] -%%     RTpidEntry={RTpid,Node}|{Error,Node} -%%     Error=term(), any term you wish to have as reply in Answer assoc. to Node. -%%   Args=[{Process,Flags},...] -%%     Process=pid()|registeredname()|'all'|'new'|'existing' -%%     Flags=List of the allowed process trace flags. -%%   RTpidsArgs=[RTpidArgEntry,...] -%%     RTpidArgEntry={RTpid,Node,Args}|{Error,Node} -%%   RTpidsArgsHow=[RTpidArgsHowEntry,...] -%%     RTpidArgsHowEntry={RTpid,Node,Args,How}|{Error,Node} -%%   How=true|false -%%   Reply={ok,Answers} -%%     Answers=[Answer,...], one for each Args and in the same order. -%%       Answer=int()|{error,Reason} -%% API function used by the control component to send flags to a list of runtime -%% components. Returns a list of [{Node,Answer},... ] in the same order. -trace_flags_parallel(RTpids,Args,How) ->     % Same args for every node! -    call_parallel(lists:map(fun({Pid,Node})when is_pid(Pid)->{Pid,Node,{tf,Args,How}}; -			       (Error)-> Error -			    end, -			    RTpids)). - -trace_flags_parallel(RTpidArgs,How) ->       % Different args but same how. -    call_parallel(lists:map(fun({Pid,Node,Args})when is_pid(Pid)-> -				    {Pid,Node,{tf,Args,How}}; -			       (Error)-> -				    Error -			    end, -			    RTpidArgs)). - -trace_flags_parallel(RTpidArgsHow) ->        % Both different args and hows. -    call_parallel(lists:map(fun({Pid,Node,Args,How})when is_pid(Pid)-> -				    {Pid,Node,{tf,Args,How}}; -			       (Error)-> -				    Error -			    end, -			    RTpidArgsHow)). -%% ------------------------------------------------------------------------------ - -%% meta_pattern(RTpids,Args)=[{Node,Answer},...] -%%   RTpids=[{RTpid,Node},...] or [{Error,Node},...] -%%   Args={FunctionName,ArgList} -%%     FunctionName=atom() -%%     ArgList=list(), list of the arguments to FunctionName. -%%   Answer=[Answer,...] -%%     Answer=int()|{error,Reason} -%% Makes a call to the meta-tracer through its runtime component. Returns a list -%% a answers in the same order as RTpids. Note that if "someone" has discovered -%% that there is an error with a particular node, the error answer can be placed -%% in the RTpids list from the start. -meta_tracer_call_parallel(RTpids,Args) ->    % Same args for all nodes. -    call_parallel(lists:map(fun({Pid,Node})when is_pid(Pid)-> -				    {Pid,Node,{meta_tracer_call,Args}}; -			       (Error)-> -				    Error -			    end, -			    RTpids)). -%% ------------------------------------------------------------------------------ - -%% get_status(Pid)={ok,{State,Status}} -%%   State=new|tracing|idle -%%   Status=running|{suspended,Reason} -get_status(Pid) -> -    call(Pid,get_status). -%% ------------------------------------------------------------------------------ - -%% get_tracerdata(Pid)={ok,TracerData} | {ok,no_tracerdata} | {error,Reason} -%%   TracerData=see init_tracing -%% Fetches the current tracerdata from the runtime component. -get_tracerdata(Pid) -> -    call(Pid,get_tracerdata). -%% ------------------------------------------------------------------------------ - -%% list_log(Pid)={ok,no_log}|{ok,LogCollection}|{error,Reason} -%% list_log(Pid,TracerData)= -%%   LogCollection=[LogTypes,...] -%%     LogTypes={trace_log,Dir,Files}|{ti_log,Dir,Files} -%%     Dir=string() -%%     Files=[FileNameWithoutDir,...] -%% Lists all files associated with the current tracerdata. Or finds out which -%% files there are stored in this node given a tracerdata. -list_logs(Pid) -> -    call(Pid,list_logs). -list_logs(Pid,TD) -> -    call(Pid,{list_logs,TD}). -%% ------------------------------------------------------------------------------ - -%% fetch_log(Pid,CollectPid)={ok,FetcherPid}|{complete,no_log}|{error,Reason} -%% fetch_log(Pid,CollectPid,Spec)= -%%   CollectPid=pid(), the process which will be given the transfered logs. -%%   Spec=TracerData|LogCollection -%% Transferes a number of files using ditributed Erlang to CollectPid. This -%% function is supposed to be used internally by a control component. It returns -%% when the transfer is initiated and does not mean it is done or successful. -fetch_log(Pid,CollectPid) -> -    call(Pid,{fetch_log,CollectPid}). -fetch_log(Pid,CollectPid,Spec) -> -    call(Pid,{fetch_log,CollectPid,Spec}). -%% ------------------------------------------------------------------------------ - -%% delete_log(Pid,TracerDataOrLogList)={ok,Results}|{error,Reason} -%%   TracerDataOrLogList=[FileNameWithPath,...]|LogCollection|TracerData -%%   Results=[LogType,...] -%%   LogType={trace_log,FileSpecs}|{ti_log,FilesSpecs} -%%     FilesSpecs=[FileSpec,...] -%%       FileSpec={ok,FileName}|{error,{Posix,FileName}} -%%       Filename=string(), the filename without dir-path. -delete_log(Pid) -> -    call(Pid,delete_logs). -delete_log(Pid,TracerDataOrLogList) -> -    call(Pid,{delete_logs,TracerDataOrLogList}). -%% ------------------------------------------------------------------------------ - -%% state(NodeOrPid)=LoopData -%% Returns the loopdata of the runtime component. Only meant for debugging. -state(NodeOrPid) -> -    call(NodeOrPid,state). -%% ------------------------------------------------------------------------------ - - -%% ============================================================================== -%% API for local calls made from the same node. E.g autostart. -%% ============================================================================== - -%% init_tracing(TracerData)= -%% See init_tracing/2. -init_tracing(TracerData) -> -    call_regname(?MODULE,{init_tracing,TracerData}). -%% ------------------------------------------------------------------------------ - - -%% Meaning that these function does most often not have to be called by a -%% control component because there are more efficient ones above. - -%% tp(Module,Function,Arity,MatchSpec) -> -%% tp(Module,Function,Arity,MatchSpec,Opts) -> -%% tp(PatternList) ->  -%%   Module = '_'|atom()|ModRegExp|{DirRegExp,ModRegExp} -%%   Function == atom() | '_' -%%   Arity = integer() | '_' -%%   MatchSpec = true | false | [] | matchspec() see ERTS User's guide for a -%%   description of match specifications. -%%   Opts=list(); 'only_loaded' -%%   PatternList = [Pattern], -%%   Pattern = {Module,Function,Arity,MatchSpec,Opts}, -%% Set trace pattern (global). -tp(Module,Function,Arity,MatchSpec) -> -    tp(Module,Function,Arity,MatchSpec,[]). -tp(Module,Function,Arity,MatchSpec,Opts) -> -    call_regname(?MODULE,{tp,[{Module,Function,Arity,MatchSpec,Opts}],[global]}). -tp(PatternList) ->  -    call_regname(?MODULE,{tp,PatternList,[global]}). -%% ------------------------------------------------------------------------------ - -tpg(Mod,Func,Arity,MatchSpec) -> -    tp(Mod,Func,Arity,MatchSpec). -tpg(Mod,Func,Arity,MatchSpec,Opts) -> -    tp(Mod,Func,Arity,MatchSpec,Opts). -tpg(PatternList) -> -    tp(PatternList). -%% ------------------------------------------------------------------------------ - -%% tpl(Module,Function,Arity,MatchSpec) -> -%% tpl(Module,Function,Arity,MatchSpec,Opts) -> -%% tpl(PatternList) -> -%%   Module = Function == atom() | '_' | RegExpMod | {RegExpDir,RegExpMod} -%%   Arity = integer() | '_' -%%   MatchSpec = true | false | [] | matchspec() see ERTS User's guide for a -%%   Opts=list(); 'only_loaded' -%%   description of match specifications. -%%   PatternList = [Pattern], -%%   Pattern = {Module, Function, Arity, MatchSpec}, -%% Set trace pattern (local). -tpl(Module,Function,Arity,MatchSpec) -> -    call_regname(?MODULE,{tp,[{Module,Function,Arity,MatchSpec,[]}],[local]}). -tpl(Module,Function,Arity,MatchSpec,Opts) -> -    call_regname(?MODULE,{tp,[{Module,Function,Arity,MatchSpec,Opts}],[local]}). -tpl(PatternList) -> -    call_regname(?MODULE,{tp,PatternList,[local]}). -%% ------------------------------------------------------------------------------ - -%% ctp(Module,Function,Arity) -> -%% ctp(PatternList)= -%%   Module = atom()|'_'|RegExpMod|{RegExpDir,RegExpMod} -%%   Function == atom() | '_' -%%   Arity = integer() | '_' -%%   PatternList=[{Mod,Func,Arity},...] -%% Clear trace pattern (global). -%% Note that it is possible to clear patterns using regexps. But we can for -%% natural reasons only clear patterns for loaded modules. Further more there -%% seems to be a fault in the emulator (<=R10B) crashing if we remove patterns -%% for deleted modules. Therefore we use the only_loaded option. -ctp(Module,Function,Arity) -> -    call_regname(?MODULE,{tp,[{Module,Function,Arity,false,[only_loaded]}],[global]}).  -ctp(PatternList) -> -    call_regname(?MODULE, -		 {tp, -		  lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList), -		  [global]}). -%% ------------------------------------------------------------------------------ - -ctpg(Mod,Func,Arity) -> -    ctp(Mod,Func,Arity). -ctpg(PatternList) -> -    ctp(PatternList). -%% ------------------------------------------------------------------------------ - -%% ctpl(Module,Function,Arity) -> -%%   Module = atom()|'_'|RegExpMod|{RegExpDir,RegExpMod} -%%   Function == atom() | '_' -%%   Arity = integer() | '_' -%%   PatternList=[{Mod,Func,Arity},...] -%% Clear trace pattern (local). -ctpl(Module,Function,Arity) -> -    call_regname(?MODULE,{tp,[{Module,Function,Arity,false,[only_loaded]}],[local]}).  -ctpl(PatternList) -> -    call_regname(?MODULE, -		 {tp, -		  lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList), -		  [local]}). -%% ------------------------------------------------------------------------------ - -init_tpm(Mod,Func,Arity,CallFunc) -> -    call_regname(?MODULE,{meta_tracer_call,{init_tpm,[Mod,Func,Arity,CallFunc]}}). - -init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> -    call_regname(?MODULE, -		 {meta_tracer_call, -		  {init_tpm, -		   [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}}). -%% ------------------------------------------------------------------------------ - -tpm(Mod,Func,Arity,MS) -> -    call_regname(?MODULE,{meta_tracer_call,{tpm,[Mod,Func,Arity,MS]}}). -tpm(Mod,Func,Arity,MS,CallFunc) -> -    call_regname(?MODULE,{meta_tracer_call,{tpm,[Mod,Func,Arity,MS,CallFunc]}}). -tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> -    call_regname(?MODULE, -		 {meta_tracer_call, -		  {tpm, -		   [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}}). -%% ------------------------------------------------------------------------------ - -tpm_tracer(Mod,Func,Arity,MS) -> -    call_regname(?MODULE,{meta_tracer_call,{tpm_tracer,[Mod,Func,Arity,MS]}}). -tpm_tracer(Mod,Func,Arity,MS,CallFunc) -> -    call_regname(?MODULE,{meta_tracer_call,{tpm_tracer,[Mod,Func,Arity,MS,CallFunc]}}). -tpm_tracer(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> -    call_regname(?MODULE, -		 {meta_tracer_call, -		  {tpm_tracer, -		   [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}}). -%% ------------------------------------------------------------------------------ - -tpm_ms(Mod,Func,Arity,MSname,MS) -> -    call_regname(?MODULE,{meta_tracer_call,{tpm_ms,[Mod,Func,Arity,MSname,MS]}}). -%% ------------------------------------------------------------------------------ - -tpm_ms_tracer(Mod,Func,Arity,MSname,MS) -> -    call_regname(?MODULE,{meta_tracer_call,{tpm_ms_tracer,[Mod,Func,Arity,MSname,MS]}}). -%% ------------------------------------------------------------------------------ - -ctpm_ms(Mod,Func,Arity,MSname) -> -    call_regname(?MODULE,{meta_tracer_call,{ctpm_ms,[Mod,Func,Arity,MSname]}}). -%% ------------------------------------------------------------------------------ - -local_register() -> -    call_regname(?MODULE,{meta_tracer_call,{local_register,[]}}). -%% ------------------------------------------------------------------------------ - -global_register() -> -    call_regname(?MODULE,{meta_tracer_call,{global_register,[]}}). -%% ------------------------------------------------------------------------------ - -ctpm(Mod,Func,Arity) -> -    call_regname(?MODULE,{meta_tracer_call,{ctpm,[Mod,Func,Arity]}}). -%% ------------------------------------------------------------------------------ - -remove_local_register() -> -    call_regname(?MODULE,{meta_tracer_call,{remove_local_register,[]}}). -%% ------------------------------------------------------------------------------ - -remove_global_register() -> -    call_regname(?MODULE,{meta_tracer_call,{remove_global_register,[]}}). -%% ------------------------------------------------------------------------------ - -%% tf(PidSpec, FlagList) -> -%% tf(TraceConfList) -> -%%   TraceConfList = [{PidSpec, FlagList}], -%%   FlagList = [Flags], -%%   PidSpec = all | new | existing | pid() | registeredname() -%%   Flags = all | send | 'receive' | procs | call | silent | return_to | -%%   running | garbage_collection | timestamp | cpu_timestamp | arity |  -%%   set_on_spawn | set_on_first_spawn | set_on_link | set_on_first_link -%% Set trace flags. -tf(PidSpec, FlagList) -> -    call_regname(?MODULE,{tf,[{PidSpec,FlagList}],true}). - -tf(TraceConfList) ->  -    call_regname(?MODULE,{tf,TraceConfList,true}). -%% ------------------------------------------------------------------------------ - -%% ctf(PidSpec, FlagList) -> -%% ctf(TraceConfList) -> -%%   TraceConfList = [{PidSpec, FlagList}], -%%   FlagList = [Flags], -%%   PidSpec = all | new | existing | pid() | registeredname() -%%   Flags = all | send | 'receive' | procs | call | silent | return_to | -%%   running | garbage_collection | timestamp | cpu_timestamp | arity |  -%%   set_on_spawn | set_on_first_spawn | set_on_link | set_on_first_link -%% Clear trace flags. -ctf(PidSpec, FlagList) -> -    call_regname(?MODULE,{tf,[{PidSpec,FlagList}],false}). - -ctf(TraceConfList) ->  -    call_regname(?MODULE,{tf_as,TraceConfList,false}). -%% ------------------------------------------------------------------------------ - - -%% ------------------------------------------------------------------------------ -%% Client side functions. -%% ------------------------------------------------------------------------------ - -%% Call function managing the client to server communication. This function may -%% be run by a client on a different node. -%% Note that we must use two different functions for calling a named process and -%% calling the runtime component at a specified node. -call(Pid,Request) when is_pid(Pid) -> -    call_2(Pid,Request); -call(Node,Request) when Node==node() ->     % To our node! -    call_2(?MODULE,Request); -call(Node,Request) when is_atom(Node) -> -    call_2({?MODULE,Node},Request); -call(To,_Request) -> -    {error,{badarg,To}}. - -call_regname(Name,Request) when is_atom(Name) -> % To a registered name. -    call_2(Name,Request). - -call_2(To,Request) -> -    MRef=erlang:monitor(process,To),        % Use a monitor to avoid waiting for ever. -    Ref=make_ref(), -    case catch To ! {Request,self(),Ref} of % Can be a remote pid. -	{'EXIT',_} ->                       % If we use registered name. -	    erlang:demonitor(MRef),         % Maybe not necessary!? -	    receive -		{'DOWN',MRef,_Type,_Obj,_Info} -> -		    true -	    after -		0 -> -		    true -	    end, -	    {error,not_started}; -	_ ->                                % At least no obvious error. -	    receive -		{Msg,Ref} -> -		    erlang:demonitor(MRef), -		    Msg; -		{'DOWN',MRef,_Type,_Obj,Info} -> % The runtime component disapeared. -		    {error,{no_response,Info}} -	    end -    end. -%% ----------------------------------------------------------------------------- - -%% Multicall function taking a list of [{Pid,Node,Request},...] and sends -%% a request to every Pid. This function then also allows you to send multiple -%% requests to the same Pid since it will sit and wait for all replies. -%% Note that RTspec may also be an [{{error,Reason},Node},...]. That tuple will -%% then be used as reply in the reply list. -%% Returns [{Node,Reply},...] for every element in RTspec, in the same order. -call_parallel(RTspec) -> -    Ref=make_ref(), -    {Nr,Pending}=call_parallel_2(RTspec,Ref,0,[]), -    Replies=call_parallel_3(Ref,Pending,Nr,[],[]), -    call_parallel_build_reply(RTspec,1,Replies). - -call_parallel_2([{Pid,Node,Request}|Rest],Ref,Nr,Pending) when is_pid(Pid) -> -    Pid ! {Request,self(),{Ref,Nr+1}}, -    MRef=erlang:monitor(process,Pid),        % So we won't wait for ever for it. -    call_parallel_2(Rest,Ref,Nr+1,[{Nr+1,Node,MRef}|Pending]); -call_parallel_2([{{error,_Reason},_Node}|Rest],Ref,Nr,Pending) -> -    call_parallel_2(Rest,Ref,Nr,Pending); % Just skip it. This is no process. -call_parallel_2([_Faulty|Rest],Ref,Nr,Pending) -> % Should not happend. -    call_parallel_2(Rest,Ref,Nr,Pending); % But we choose to skip it instead of crash. -call_parallel_2([],_,Nr,Pending) -> -    {Nr,Pending}. - -%% Help function collecting reply-messages sent from the runtime components. We -%% count down until we got a reply for every pending request. Or if we get a DOWN -%% message indicating that the runtime component is no longer present. Note that -%% we can by accident read away DOWN messages not belonging to this procedure. -%% They are collected to be reissued after we are done. -call_parallel_3(_Ref,_Pending,0,Replies,DownMsgs) ->  % All expected received. -    lists:foreach(fun({MRef,Pid,Info}) -> self() ! {'DOWN',MRef,process,Pid,Info} end, -		  DownMsgs),                 % Reissue the down messages! -    Replies; -call_parallel_3(Ref,Pending,NrOfPending,Replies,DownMsgs) -> -    receive -	{Reply,{Ref,Nr}} -> -	    case lists:keysearch(Nr,1,Pending) of -		{value,{_Nr,Node,MRef}} -> -		    erlang:demonitor(MRef), -		    call_parallel_3(Ref,Pending,NrOfPending-1, -				    [{Nr,Node,Reply}|Replies],DownMsgs); -		false ->                     % Really strange! -		    call_parallel_3(Ref,Pending,NrOfPending,Replies,DownMsgs) -	    end; -	{'DOWN',MRef,process,Pid,Info} ->    % Probably process we monitor terminated. -	    case lists:keysearch(MRef,3,Pending) of -		{value,{Nr,Node,_}} ->       % Yes it was one of our processes. -		    call_parallel_3(Ref,Pending,NrOfPending-1, -				    [{Nr,Node,{error,no_reponse}}|Replies],DownMsgs); -		false ->                     % We picked up a DOWN msg by misstake. -		    call_parallel_3(Ref,Pending,NrOfPending,Replies, -				    [{MRef,Pid,Info}|DownMsgs]) -	    end -    end. - -%% Help function which build up the [{Node,Reply},...] list in the same order as RTspec. -call_parallel_build_reply([],_,_) -> -    []; -call_parallel_build_reply([{Pid,Node,_Request}|Rest],Nr,Replies) when is_pid(Pid) -> -    {value,{_Nr,_Node,Reply}}=lists:keysearch(Nr,1,Replies), -    [{Node,Reply}|call_parallel_build_reply(Rest,Nr+1,Replies)]; -call_parallel_build_reply([{{error,Reason},Node}|Rest],Nr,Replies) -> -    [{Node,{error,Reason}}|call_parallel_build_reply(Rest,Nr,Replies)]; -call_parallel_build_reply([_Faulty|Rest],Nr,Replies) -> -    call_parallel_build_reply(Rest,Nr,Replies). -%% ------------------------------------------------------------------------------ - -cast(Pid,Request) when is_pid(Pid) -> -    cast2(Pid,Request); -cast(Node,Request) when Node==node() -> -    catch cast2(?MODULE,Request), -    ok; -cast(Node,Request) when is_atom(Node) -> -    catch cast2({?MODULE,Node},Request), -    ok; -cast(BadAddress,_Request) -> -    {error,{badarg,BadAddress}}. - -cast2(To,Request) -> -    To ! {Request,void,void}.                % Mimics the call protocol. -%% ------------------------------------------------------------------------------ - - -%% ============================================================================== -%% Implementation of the runtime component (server side). -%% ============================================================================== - -%% Since the runtime component is not implemented using gen_sever we are "free" -%% to use what ever functionnames we like. - -%% Initial function on which the runtime component is spawned on if started by -%% a controlcomponent. -init(Ctrl, Options, Tag, Parent) when is_list(Options) -> -    %% started from controller -    process_flag(trap_exit,true), -    register(?MODULE,self()),                % Will crash if rt is already running -    do_clear_trace_patterns(),               % Remove potential old patterns left. -    LD1=read_option_list(Options, -			 #rt{state=new, -			     parent=Parent, -			     ctrl=Ctrl, -			     vsn=get_application_vsn(), -			     tag=Tag}), -    OverloadData=initialize_overload(LD1), -    CtrlRef=erlang:monitor(process,Ctrl),    % Monitor our control component. -    loop1(LD1#rt{ctrl_ref=CtrlRef,overload_data=OverloadData}). -%% ---------------------------------------------------------------------------- - -%% Initial function on which the runtime component is spawned on if started -%% by the runtime_tools supervisor. It is here it is determined if we shall -%% autostart. -auto_init(AutoModArgs,Parent) ->  -    %% autostart -    process_flag(trap_exit, true), -    register(?MODULE, self()),               % Will crash if a rt is already running -    AutoMod=get_autostart_module(),          % Determine which module to use! -    case catch AutoMod:autostart(AutoModArgs) of  -	{MFA,Options,Tag} -> -	    do_clear_trace_patterns(),       % Remove previously left patterns. -	    LD1=read_option_list(Options,#rt{state=new, -					     parent=Parent, -					     vsn=get_application_vsn(), -					     tag=Tag}), -	    case auto_init_connect_control(LD1) of -		{ok,LD2} ->                  % Either connected or running_alone. -		    OverloadData=initialize_overload(LD2), -		    case auto_init_check_mfa(MFA) of -			{ok,{M,F,A}} ->      % We shall start somekind of tracing! -			    P=spawn_link(M,F,A), % It lives its own life, only link! -			    loop1(LD2#rt{auto_starter=P,overload_data=OverloadData}); -			false -> -			    loop1(LD2#rt{overload_data=OverloadData}) -		    end; -		stop ->                      % Not allowed to run alone! -		    true                     % Simply terminate. -	    end; -	 _ ->                                % Non existent or faulty autostart mod! -	    true                             % Terminate normally. -    end. - -auto_init_connect_control(LD1) -> -    case auto_init_connect_find_pid(LD1#rt.dependency) of -	Pid when is_pid(Pid) ->                 % There is a control component. -	    CtrlRef=erlang:monitor(process,Pid), -	    Pid ! {connect,node(),self(),LD1#rt.vsn,LD1#rt.tag}, -	    {ok,LD1#rt{ctrl_ref=CtrlRef,ctrl=Pid}}; -	_ ->                                 % There is no control component. -	    do_down_message(LD1)             % Will return 'stop' or a LoopData. -    end. - -%% Help function which finds the pid of the control component. -auto_init_connect_find_pid({_TimeOut,Node}) when Node==node() -> -    whereis(?CTRL); -auto_init_connect_find_pid({_TimeOut,Node}) when is_atom(Node) -> -    rpc:call(Node,erlang,whereis,[?CTRL]); -auto_init_connect_find_pid(_) ->             % Node is not a proper node. -    undefined.                               % Act as could not find control comp. - -%% Help function checking that the parameter is reasonable to be used as -%% spawn_link argument. -auto_init_check_mfa({M,F,A}) when is_atom(M),is_atom(F),is_list(A) -> -    {ok,{M,F,A}}; -auto_init_check_mfa(_) -> -    false. - -%% Help function to init_auto which finds out which module to call for -%% guidance on how to proceed. Returns an atom. -get_autostart_module() -> -    case application:get_env(inviso_autostart_mod) of -	{ok,Mod} when is_atom(Mod) -> -	    Mod; -	_ -> -	    inviso_autostart                 % The default autostart module. -    end. -%% ---------------------------------------------------------------------------- - - -%% This is the preloop function which performs loadcheck if necessary. Note -%% that it calculates the timeout used in the after in the real loop. There is -%% further no use doing overload checks if we are not tracing or already -%% suspended. There is yet one more situation, we do not want to perform -%% overload checks if the interval is set to infinity. This can be the case if -%% we are using an external source pushing overload information instead. -loop1(LD=#rt{overload=Overload}) -> -    if -	Overload/=?NO_LOADCHECK,element(2,Overload)/=infinity -> -	    Now=now(), -	    if -		LD#rt.status==running, -		LD#rt.state==tracing, -		Now>LD#rt.next_loadcheck ->  % Do loadcheck only then! -		    {NewLD,TimeOut}=do_check_overload(LD,{timeout,LD#rt.overload_data}), -		    loop(NewLD,TimeOut); -		LD#rt.status==running,LD#rt.state==tracing -> -		    Timeout=calc_diff_to_now(Now,LD#rt.next_loadcheck), -		    loop(LD,Timeout); -		true ->                      % Do not spend CPU on this! :-) -		    loop(LD,infinity) -	    end; -	true ->                              % Either no check or infinity. -	    loop(LD,infinity) -    end. - -loop(LoopData,Timeout) -> -    receive -	Msg when element(1,Msg)==trace_ts; -		 element(1,Msg)==trace; -		 element(1,Msg)==drop; -		 element(1,Msg)==seq_trace -> -	    case LoopData#rt.handler of -		{HandlerFun,Data} -> -		    NewData=HandlerFun(Msg,Data), -		    loop1(LoopData#rt{handler={HandlerFun,NewData}}); -		_ -> -		    loop1(LoopData) -	    end; -	{{tp,Args,Flags},From,Ref} -> -	    if -		LoopData#rt.status==running -> % Not when suspended. -		    Reply=do_set_trace_patterns(Args,Flags), -		    if -			LoopData#rt.state==new -> % No longer new when tp set. -			    reply_and_loop({ok,Reply},From,Ref,LoopData#rt{state=idle}); -			true -> -			    reply_and_loop({ok,Reply},From,Ref,LoopData) -		    end; -		true ->                      % We are suspended! -		    reply_and_loop({error,suspended},From,Ref,LoopData) -	    end; -	{{tf,Args,How},From,MRef} -> -	    Reply= -		case How of -		    true -> -			if -			    LoopData#rt.status==running ->  -				case {LoopData#rt.tracer_port,LoopData#rt.handler} of -				    {Port,_} when is_port(Port) -> -					do_set_trace_flags(Port,Args,How); -				    {_,{Handler,_D}} when is_function(Handler) -> -					do_set_trace_flags(self(),Args,How); -				    _ ->  -					{error,no_tracer} -				end; -			    true ->          % Can't turn *on* flags if suspended. -				{error, suspended} -			end; -		    false ->                 % No tracer needed when turning off. -			do_set_trace_flags(void,Args,How) -		end, -	    reply_and_loop(Reply,From,MRef,LoopData); -	{{meta_tracer_call,Args},From,MRef} -> -	    if -		LoopData#rt.status==running -> -		    case LoopData#rt.meta_tracer of -			MPid when is_pid(MPid) -> -			    Reply=do_meta_pattern(MPid,Args), -			    reply_and_loop(Reply,From,MRef,LoopData); -			_ -> -			    reply_and_loop({error,no_metatracer},From,MRef,LoopData) -		    end; -		true -> -		    reply_and_loop({error,suspended},From,MRef,LoopData) -	    end; -	{clear_all_tp,From,MRef} -> -	    do_clear_trace_patterns(),	     -	    reply_and_loop(ok,From,MRef,LoopData); -	{{init_tracing,TracerData},From,MRef} -> -	    {NewLoopData,Reply}=  -		if -		    LoopData#rt.status==running -> -			if -			    LoopData#rt.state==tracing -> -				{LoopData,{error,already_initiated}}; -			    true ->          % Otherwise, try to init-tracing! -				case translate_td(TracerData) of -				    {ok,LogTD,MetaTD} -> -					do_init_tracing(LoopData,TracerData,LogTD,MetaTD); -				    Error -> -					{LoopData,Error} -				end -			end; -		    true ->                  % Can't init tracing if not running. -			{LoopData,{error,suspended}} -		end, -	    reply_and_loop(Reply,From,MRef,NewLoopData); -	{stop_tracing,From,MRef} -> -	    case LoopData#rt.state of -		tracing ->                   % Only case we need to do anything. -		    reply_and_loop({ok,idle},From,MRef,do_stop_tracing(LoopData)); -		idle ->                      % Already idle! -		    reply_and_loop({ok,idle},From,MRef,LoopData); -		new ->                       % Have actually never traced! -		    reply_and_loop({ok,new},From,MRef,LoopData) -	    end; -	{{suspend,Reason},From,MRef} -> -	    if -		LoopData#rt.status==running -> -		    NewLD=do_suspend(LoopData,Reason), -		    reply_and_loop(ok,From,MRef,NewLD); -		true ->                      % No need suspend if not running! -		    reply_and_loop(ok,From,MRef,LoopData) -	    end; -	{cancel_suspension,From,MRef} -> -	    NewLoopData=LoopData#rt{status=running,next_loadcheck=now()}, -	    send_event(state_change,NewLoopData), -	    reply_and_loop(ok,From,MRef,NewLoopData); -	{{clear,Options},From,MRef} -> -	    NewLoopData=do_clear(LoopData,Options), -	    reply_and_loop({ok,{new,NewLoopData#rt.status}},From,MRef,NewLoopData); -	{flush,From,MRef} -> -	    case LoopData#rt.state of -		tracing ->                  % Can only flush if we are tracing. -		    if -			is_port(LoopData#rt.tracer_port) -> -			    trace_port_control(LoopData#rt.tracer_port,flush), -			    reply_and_loop(ok,From,MRef,LoopData); -			true ->             % Not necessary but lets pretend. -			    reply_and_loop(ok,From,MRef,LoopData) -		    end; -		State -> -		    reply_and_loop({error,{not_tracing,State}},From,MRef,LoopData) -	    end; -	{list_logs,From,MRef} -> -	    TracerData=LoopData#rt.tracerdata, % Current tracerdata. -	    if -		TracerData/=undefined ->    % There is tracerdata! -		    reply_and_loop(do_list_logs(TracerData),From,MRef,LoopData); -		true ->                     % Have no current tracerdata! -		    reply_and_loop({error,no_tracerdata},From,MRef,LoopData) -	    end; -	{{list_logs,TracerData},From,MRef} -> -	    reply_and_loop(do_list_logs(TracerData),From,MRef,LoopData); -	{{fetch_log,CollectPid},From,MRef} -> % Fetch according to current tracerdata. -	    TracerData=LoopData#rt.tracerdata, % Current tracerdata. -	    if -		TracerData/=undefined ->    % There is tracerdata! -		    {Reply,NewLD}=do_fetch_log(LoopData,CollectPid,TracerData), -		    reply_and_loop(Reply,From,MRef,NewLD); -		true ->                     % No tracerdata! -		    reply_and_loop({error,no_tracerdata},From,MRef,LoopData) -	    end; -	{{fetch_log,CollectPid,Spec},From,MRef} -> % Either list of files or tracerdata. -	    {Reply,NewLD}=do_fetch_log(LoopData,CollectPid,Spec), -	    reply_and_loop(Reply,From,MRef,NewLD); -	{delete_logs,From,MRef} -> -	    if -		LoopData#rt.state==tracing -> % Can't remove then! -		    reply_and_loop({error,tracing},From,MRef,LoopData); -		true -> -		    TracerData=LoopData#rt.tracerdata, -		    reply_and_loop(do_delete_logs(TracerData),From,MRef,LoopData) -	    end; -	{{delete_logs,TracerDataOrLogList},From,MRef} -> -	    if -		LoopData#rt.state==tracing -> % Can't remove then! -		    reply_and_loop({error,tracing},From,MRef,LoopData); -		true -> -		    reply_and_loop(do_delete_logs(TracerDataOrLogList),From,MRef,LoopData) -	    end; -	{get_node_info,From,MRef} -> -	    Reply=collect_node_info(LoopData), -	    reply_and_loop(Reply,From,MRef,LoopData); -	{{try_to_adopt,Tag,Condition},From,MRef} -> -	    if -		LoopData#rt.ctrl_ref==undefined -> % We have no control component. -		    {Reply,NewLoopData}=do_try_to_adopt(Tag,Condition,LoopData,From), -		    reply_and_loop(Reply,From,MRef,NewLoopData); -		true ->                      % We already have a control component. -		    reply_and_loop({error,refused},From,MRef,LoopData) -	    end; -	{{confirm_connection,_Tag},From,MRef} -> -	    if -		LoopData#rt.ctrl==From ->    % It must be from this process! -		    Reply=collect_node_info(LoopData), -		    reply_and_loop(Reply,From,MRef,LoopData); -		true ->                      % Strange, some one is joking? -		    reply_and_loop({error,refused},From,MRef,LoopData) -	    end; -	{{change_options,Options},From,MRef} -> -	    case do_change_options(Options,LoopData) of -		stop ->                      % Can't run alone with these options! -		    terminate_overload(LoopData), -		    From ! {ok,MRef};        % Don't care if From not a proper pid! -		NewLoopData when is_record(NewLoopData,rt) -> -		    reply_and_loop(ok,From,MRef,NewLoopData) -	    end; -	{get_status,From,MRef} -> -	    Reply={ok,{LoopData#rt.state,LoopData#rt.status}}, -	    reply_and_loop(Reply,From,MRef,LoopData); -	{get_tracerdata,From,MRef} -> -	    case LoopData#rt.tracerdata of -		undefined -> -		    reply_and_loop({ok,no_tracerdata},From,MRef,LoopData); -		TracerData -> -		    reply_and_loop({ok,TracerData},From,MRef,LoopData) -	    end; -	{state,From,MRef} ->                 % For debugging purposes. -	    reply_and_loop(LoopData,From,MRef,LoopData); - -	{'DOWN',CtrlRef,process,_,_} when CtrlRef==LoopData#rt.ctrl_ref -> -	    case do_down_message(LoopData) of -		stop ->                      % inviso_c gone and we must stop! -		    terminate_overload(LoopData), -		    exit(running_alone); -		{ok,NewLoopData} -> -		    loop1(NewLoopData) -	    end; -	{'EXIT',Pid,Reason} -> -	    case act_on_exit(Pid,Reason,LoopData) of -		exit -> -		    terminate_overload(LoopData), -		    exit(Reason); -		NewLoopData when is_record(NewLoopData,rt) -> -		    loop1(NewLoopData); -		{NewLoopData,NewTimeOut} when is_record(NewLoopData,rt) -> -		    loop(NewLoopData,NewTimeOut) -	    end; -	Other ->                            % Check if it concerns overload. -	    if -		LoopData#rt.overload/=?NO_LOADCHECK, -		LoopData#rt.status==running, -		LoopData#rt.state==tracing -> -		    {NewLD,NewTimeOut}= -			do_check_overload(LoopData, -					  {msg,{Other,LoopData#rt.overload_data}}), -		    loop(NewLD,NewTimeOut); -		true -> -		    NewTimeOut=calc_diff_to_now(now(),LoopData#rt.next_loadcheck), -		    loop(LoopData,NewTimeOut) -	    end -    after -	Timeout -> -	    loop1(LoopData) -    end. - -reply_and_loop(Reply,To,MRef,LoopData) when is_pid(To) ->  -    To ! {Reply,MRef}, -    loop1(LoopData); -reply_and_loop(_,_,_,LoopData) ->            % Used together with incoming casts. -    loop1(LoopData). -%% ----------------------------------------------------------------------------- - - -%% ============================================================================= -%% File transfer process implementation. -%% ============================================================================= - -%% Files that are to to be transfered from the runtime component to the control -%% component are done so by reading them as binaries and sending them with -%% normal message passing (over distributed Erlang). -%% Reading the files are done in a process separate to the runtime component, -%% to both make the code more simple. But also to free up the runtime component. -%% -%% This help process must be capable of recognizing the fact that the runtime -%% component has been suspended, and then of course also discontinue any file -%% transfere. -fetch_init(Parent,Files,CollectPid,ChunkSize) -> -    process_flag(trap_exit,true),            % We must clean-up. -    process_flag(priority,low),              % Lets be careful. -    case fetch_open_file(Files,CollectPid) of -	{ok,FileName,FD,RestFiles} -> -	    MRef=erlang:monitor(process,CollectPid), -	    fetch_loop(Parent,RestFiles,CollectPid,ChunkSize,FileName,FD,MRef); -	done -> -	    fetch_end(CollectPid); -	error -> -	    fetch_incomplete(CollectPid) -    end. - -fetch_loop(Parent,Files,CollectPid,ChunkSize,FName,FD,MRef) -> -    receive -	{suspend,Parent} ->                  % The runtime component is suspended. -	    file:close(FD),                  % We must clean-up. -	    fetch_incomplete(CollectPid); -	{'DOWN',MRef,process,_,_} ->         % The CollectPid terminated! -	    file:close(FD);                  % Close file and terminate. -	{'EXIT',Parent,_Reason} ->           % The runtime component terminated. -	    file:close(FD), -	    fetch_incomplete(CollectPid); -	_ -> -	    fetch_loop(Parent,Files,CollectPid,ChunkSize,FName,FD,MRef) -    after -	0 ->                                 % If non of the above, get to work! -	    case file:read(FD,ChunkSize) of -		{ok,Bin} -> -		    fetch_send_chunk(CollectPid,Bin), -		    case fetch_wait_for_chunk_ack(CollectPid,MRef) of -			ok ->                % Collector ready to receive next chunk. -			    fetch_loop(Parent,Files,CollectPid,ChunkSize,FName,FD,MRef); -			cancel ->            % Send no more files! -			    file:close(FD),  % Close file, send incomplete, terminate! -			    fetch_incomplete(CollectPid); -			'DOWN' ->            % Collector has terminate, stop! -			    file:close(FD)   % Close file and terminate. -		    end; -		eof ->                       % Ok, go on with the next file. -		    file:close(FD), -		    fetch_send_eof(CollectPid), -		    case fetch_open_file(Files,CollectPid) of -			{ok,NewFName,NewFD,RestFiles} -> -			    fetch_loop(Parent,RestFiles,CollectPid, -				       ChunkSize,NewFName,NewFD,MRef); -			done -> -			    fetch_end(CollectPid); -			error -> -			    fetch_incomplete(CollectPid) -		    end; -		{error,Reason} ->            % Do not continue. -		    file:close(FD), -		    fetch_send_readerror(CollectPid,FName,Reason), -		    fetch_incomplete(CollectPid) -	    end -    end. -%% ----------------------------------------------------------------------------- - -%% Help function which opens the next file to be transferred. It also communicates -%% the opening of the file to the collector process. -%% We know here that it will be a list of three-tuples. But there is no guarantee -%% that Dir or FileName are proper strings. -%% Returns {ok,FileName,FileDescriptor,RemainingFiles} or 'done'. -fetch_open_file([{FType,Dir,FileName}|RestFiles],CollectPid) -> -    case catch file:open(filename:join(Dir,FileName),[read,raw,binary]) of -	{ok,FD} -> -	    CollectPid ! {node(),open,{FType,FileName}}, -	    {ok,FileName,FD,RestFiles}; -	{error,_Reason} -> -	    CollectPid ! {node(),open_failure,{FType,FileName}}, -	    error; -	{'EXIT',_Reason} ->                   % Faulty Dir or FileName. -	    CollectPid ! {node(),open_failure,{FType,FileName}}, -	    error -    end;     -fetch_open_file([],_CollectPid) -> -    done. -%% ----------------------------------------------------------------------------- - -%% A group of help functions sending information to the collector process. -%% Returns nothing significant. -fetch_send_chunk(CollectPid,Bin) -> -    CollectPid ! {node(),payload,Bin,self()}. -%% ----------------------------------------------------------------------------- - -fetch_send_eof(CollectPid) -> -    CollectPid ! {node(),end_of_file}. -%% ----------------------------------------------------------------------------- - -fetch_end(CollectPid) -> -    CollectPid ! {node(),end_of_transmission}. -%% ----------------------------------------------------------------------------- - -fetch_send_readerror(CollectPid,FName,Reason) -> -    CollectPid ! {node(),{error,{file_read,{Reason,FName}}}}. -%% ----------------------------------------------------------------------------- - -fetch_incomplete(CollectPid) -> -    CollectPid ! {node(),incomplete}. -%% ----------------------------------------------------------------------------- - -%% Help function waiting for the collector to respond that it is ready to receive -%% the next chunk. This is in order to exercise flow control protecting the -%% collector to get swamped if the node where the collector runs is busy. -fetch_wait_for_chunk_ack(CollectPid,MRef) -> -    receive -	{CollectPid,chunk_ack} -> -	    ok; -	{CollectPid,cancel_transmission} ->  % Some problem at collector side. -	    cancel; -	{'DOWN',MRef,process,_,_} ->         % The collector terminated. -	    'DOWN' -    end. -%% ----------------------------------------------------------------------------- - - -%% ============================================================================= -%% First level do-functions, called from the main server loop on incomming -%% requests. -%% ============================================================================= - -%% Function performing the overload check. Returns {NewLoopData,TimeOut}. -%% Note that this function may also cause a suspend to be carried out if the -%% loadcheck turns out negative. -do_check_overload(LD,Data) -> -    case do_check_overload_2(LD#rt.overload,Data) of -	ignore ->                            % Load check not performed. -	    {LD,calc_diff_to_now(now(),LD#rt.next_loadcheck)}; -	{ok,Interval} ->                     % No problem, continue. -	    NextLoadCheck=add_to_now(now(),Interval), -	    {LD#rt{next_loadcheck=NextLoadCheck},Interval}; -	{suspend,Reason} ->                  % Emergency! suspend, suspend! -	    NewLD=do_suspend(LD,Reason), -	    {NewLD,infinity};                % No need to do load-checks now! -	{new,NewData,Interval} ->            % The overload was restarted or something. -	    NextLoadCheck=add_to_now(now(),Interval), -	    {LD#rt{overload_data=NewData,next_loadcheck=NextLoadCheck},Interval}; -	error ->                             % Inhibit overload check then. -	    {LD#rt{overload=?NO_LOADCHECK},infinity} -    end. - -%% Help function performing an overload check. Returns {ok,Interval}, -%% {suspend,Reason}, 'error' ir 'ignore'. -do_check_overload_2({{Mod,Func},Interval,_,_},Data) -> -    do_check_overload_3(Interval,catch Mod:Func(Data)); -do_check_overload_2({Fun,Interval,_,_},Data) when is_function(Fun) -> -    do_check_overload_3(Interval,catch Fun(Data)); -do_check_overload_2(_,_) ->                  % Bad loadcheck configuration. -    error.                                   % Stop using load checks then. - -do_check_overload_3(Interval,ok) -> -    {ok,Interval}; -do_check_overload_3(Interval,{new,NewData}) -> -    {new,NewData,Interval}; -do_check_overload_3(_Interval,{suspend,Reason}) -> -    {suspend,Reason}; -do_check_overload_3(_Interval,ignore) ->     % Loadcheck not triggered. -    ignore; -do_check_overload_3(_Interval,_) ->          % Failure or other return value. -    error.                                   % Stop doing loadchecks from now on. -%% ------------------------------------------------------------------------------ - -%% Function setting the trace-pattern according to Args and Flags. Note that -%% Args can contain regexps which must be expanded here. -%% Returns a list: [Result], where Result can be: int()|{error,Reason}. -%% Sometimes an error tuple will represent an entire pattern, sometimes the -%% pattern will expand to a number of error-tuples. -do_set_trace_patterns(Args,Flags) -> -    Replies=do_set_trace_patterns_2(Args,Flags,[]), -    lists:reverse(Replies). - -do_set_trace_patterns_2([{M,F,Arity,MS}|Rest],Flags,Replies) -> % Option-less. -    do_set_trace_patterns_2([{M,F,Arity,MS,[]}|Rest],Flags,Replies); -do_set_trace_patterns_2(Mlist = [{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_atom(M) -> -    case length(Mlist) rem 10 of -	0 -> -	    timer:sleep(100); -	_ -> -	    ok -    end, -    %% sleep 100 ms for every 10:th element in the list to let other  -    %% processes run since this is a potentially -    %% heavy operation that might result in an unresponsive Erlang VM for -    %% several seconds otherwise -    case load_module_on_option(M,Opts) of -	true ->                             % Already present, loaded or no option! -	    case catch erlang:trace_pattern({M,F,Arity},MS,Flags) of -		No when is_integer(No) -> -		    do_set_trace_patterns_2(Rest,Flags,[No|Replies]); -		{'EXIT',Reason} -> -		    do_set_trace_patterns_2(Rest, -					    Flags, -					    [{error,{bad_trace_args,[{M,F,Arity,MS},Reason]}}| -					     Replies]) -	    end; -	false ->                            % Module not present, or not found! -	    do_set_trace_patterns_2(Rest,Flags,[0|Replies]) -    end; -do_set_trace_patterns_2([{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_list(M) -> -    do_set_trace_patterns_2([{{void,M},F,Arity,MS,Opts}|Rest],Flags,Replies); -do_set_trace_patterns_2([{{Dir,M},F,Arity,MS,Opts}|Rest],Flags,Replies) -  when is_list(Dir),is_list(M) -> -    case check_pattern_parameters('_',F,Arity,MS) of % We don't want to repeat bad params. -	true -> -	    case inviso_rt_lib:expand_regexp(Dir,M,Opts) of % Get a list of real modulnames. -		Mods when is_list(Mods) -> -		    MoreReplies= -			do_set_trace_patterns_2(lists:map(fun(Mod)-> -								  {Mod,F,Arity,MS,Opts} -							  end, -							  Mods), -						Flags, -						Replies), -		    do_set_trace_patterns_2(Rest,Flags,MoreReplies); -		{error,Reason} -> -		    do_set_trace_patterns_2(Rest,Flags,[{error,Reason}|Replies]) -	    end; -	false ->                            % Bad pattern parameters. -	    do_set_trace_patterns_2(Rest, -				    Flags, -				    [{error,{bad_trace_args,{M,F,Arity,MS}}}|Replies]) -    end; -do_set_trace_patterns_2([Arg|Rest],Flags,Replies) -> -    do_set_trace_patterns_2(Rest,Flags,[{error,{bad_trace_args,Arg}}|Replies]); -do_set_trace_patterns_2([],_Flags,Replies) -> -    Replies. -%% ----------------------------------------------------------------------------- - -%% Help function which sets the trace flags for all processes specifed in Args. -%% Args shall be a list of {ProcessSpecification,ProcessTraceFlags}. -%% Returns {ok,Answers} where Answers is a list of integer and error descriptions. -%% Note that a process specification may be a particular pid or a {global,Name}. -%% In the case the process does not exist we will fake a zero instead of an -%% error. -do_set_trace_flags(Tracer,Args,How) -> -    Fun=fun({Proc,Flags}) -> -		case check_traceflag_pidspec(Proc) of -		    {ok,Proc2} ->            % Reg-names converted. -			case check_flags(Flags) of -			    Flags2 when is_list(Flags2) -> % No error! -				case (catch -					 case How of -					     true -> -						 erlang:trace(Proc2, -							      true, -							      [{tracer,Tracer}|Flags2]); -					     false -> % No tracer of turning off. -						 erlang:trace(Proc2, -							      false, -							      Flags2) -					 end) of -				    N when is_integer(N) -> -					N; -				    {'EXIT',Reason} -> -					if -					    is_pid(Proc2) -> -						0;    % Proc2 not alive or not at this node! -					    true ->   % Otherwise, just error! -						{error, -						 {bad_trace_args, -						  [Reason,Proc2,How,Flags2,Tracer]}} -					end -				end; -			    FlagError -> -				FlagError -			end; -		    false ->                 % Skip it. -			0;                   % Indicate that zero processes matched. -		    {error,Reason} ->        % Bad process specification. -			{error,{bad_process,[Reason,Proc]}} -		end; -	   (Faulty) -> -		{error,{bad_process,Faulty}} -	end, -    {ok,lists:map(Fun,Args)}. -%% ------------------------------------------------------------------------------ - -%% Function calling API:s in the trace information server. Note that we have -%% given the responsibility to form a correct functionsname and argument list -%% to the caller. -%% Returns whatever the called function returns. -do_meta_pattern(MPid,{FuncName,ArgList}) -> -    case catch apply(inviso_rt_meta,FuncName,[MPid|ArgList]) of -	{'EXIT',_Reason} -> -	    {error,{badarg,{FuncName,ArgList}}}; -	Result -> -	    Result -    end; -do_meta_pattern(_MPid,BadArgs) -> -    {error,{bad_args,BadArgs}}. -%% ------------------------------------------------------------------------------ - -%% Function removing *all* patterns. Beaware that the one for local patterns -%% causes a walkthrough of all loaded modules. -do_clear_trace_patterns() -> -    erlang:trace_pattern({'_','_','_'},false,[local]), %% inc. meta, call_count -    erlang:trace_pattern({'_','_','_'},false,[global]). -%% ------------------------------------------------------------------------------ - -%% Function that takes TracerData and initializes the tracing. That can be -%% opening appropriate logfiles, starting meta-tracer. There must be one -%% clause here for every "type" of logging we want to be able to do. -%% Returns the Reply to be forwarded to the caller. -do_init_tracing(LoopData,TD,{HandlerFun,Data},TiTD) when is_function(HandlerFun) -> -    {NewLoopData,Reply}= -	case do_init_metatracing(TiTD,self()) of -	    {ok,MetaPid} -> -		{LoopData#rt{handler={HandlerFun,Data}, -			     tracerdata=TD, -			     meta_tracer=MetaPid, -			     state=tracing}, -		 {ok,[{trace_log,ok},{ti_log,ok}]}}; -	    false ->                         % No meta tracing requested. -		{LoopData#rt{handler={HandlerFun,Data}, -			     tracerdata=TD, -			     state=tracing}, -		 {ok,[{trace_log,ok}]}}; -	    {error,Reason} ->                % Problems starting meta tracing. -		{LoopData#rt{handler={HandlerFun,Data}, -			     tracerdata=TD, -			     state=tracing}, -		 {ok,[{trace_log,ok},{ti_log,{error,Reason}}]}} -	end, -    send_event(state_change,NewLoopData),    % Send to subscribing processes. -    {NewLoopData,Reply}; -do_init_tracing(LoopData,TD,{Type,Parameters},TiTD) when Type==ip;Type==file -> -    case check_traceport_parameters(Type,Parameters) of -	ok -> -	    case catch trace_port(Type,Parameters) of -		Fun when is_function(Fun) -> -		    case catch Fun() of -			Port when is_port(Port) -> % Ok, our trace-port is open. -			    {NewLoopData,Reply}= -				case do_init_metatracing(TiTD,Port) of -				    {ok,MetaPid} -> -					{LoopData#rt{tracer_port=Port, -						     tracerdata=TD, -						     meta_tracer=MetaPid, -						     state=tracing}, -					 {ok,[{trace_log,ok},{ti_log,ok}]}}; -				    false -> % No meta tracing requested. -					{LoopData#rt{tracer_port=Port, -						     tracerdata=TD, -						     state=tracing}, -					 {ok,[{trace_log,ok}]}}; -				    {error,Reason} -> % Problems starting meta tracing. -					{LoopData#rt{tracer_port=Port, -						     tracerdata=TD, -						     state=tracing}, -					 {ok,[{trace_log,ok},{ti_log,{error,Reason}}]}} -				end, -			    send_event(state_change,NewLoopData), -			    {NewLoopData,Reply}; -			{'EXIT',Reason} -> -			    {LoopData,{error,{bad_port_fun,[Parameters,Reason]}}} -		    end; -		{'EXIT',Reason} -> -		    {LoopData,{error,{bad_port_args,[Parameters,Reason]}}} -	    end; -	{error,Reason} ->                    % Bad traceport parameters. -	    {LoopData,{error,Reason}} -    end. - -%% Help function that starts the meta-tracing. Note that the runtime component -%% will becom linked to it. -%% Currently the meta tracer handles two types, 'file' and 'relay'. -%% Note that Tracer tells the meta tracer where regular trace messages shall be -%% sent. This is because the meta tracer is capable of appending a {tracer,Tracer} -%% action term to meta match specs. -do_init_metatracing(LogSpec={_Type,_Arg},Tracer) -> -    case inviso_rt_meta:start(LogSpec,Tracer) of -	{ok,MetaPid} -> -	    {ok,MetaPid}; -	{error,Reason} -> -	    {error,Reason} -    end; -do_init_metatracing({Type,Arg,{InitPublLDmfa,RemovePublLDmf,CleanPublLDmf}},Tracer)-> -    case inviso_rt_meta:start({Type,Arg},Tracer,InitPublLDmfa,RemovePublLDmf,CleanPublLDmf) of -	{ok,MetaPid} -> -	    {ok,MetaPid}; -	{error,Reason} -> -	    {error,Reason} -    end; -do_init_metatracing(void,_) ->              % Means no meta tracer. -    false. -%% ----------------------------------------------------------------------------- - -%% Function that stops all tracing and closes all open files. This function -%% can't fail :-) It tries as hard as it can. -%% This function also kills the autostarter process if one exists. Otherwise it -%% will not be possible from a control component to end an ongoing autostarted -%% tracing. -%% Returns a new loopdata structure since stopping tracing involves updating it. -do_stop_tracing(LoopData) -> -    do_stop_tracing_kill_autostarter(LoopData#rt.auto_starter), -    do_clear_trace_flags(),                 % Do not generate any more traces. -    NewLoopData1=do_stop_tracing_tracelog(LoopData), -    NewLoopData2=do_stop_tracing_metatracing(NewLoopData1), -    NewLoopData3=NewLoopData2#rt{state=idle,auto_starter=undefined}, -    send_event(state_change,NewLoopData3), -    NewLoopData3. - -do_stop_tracing_tracelog(LoopData=#rt{tracer_port=Port}) when is_port(Port) -> -    trace_port_control(Port,flush),         % Write buffered trace messages. -    catch port_close(Port), -    LoopData#rt{tracer_port=undefined}; -do_stop_tracing_tracelog(LoopData) -> -    LoopData#rt{handler=undefined}. - -do_stop_tracing_metatracing(LoopData=#rt{meta_tracer=MPid}) when is_pid(MPid) -> -    inviso_rt_meta:stop(MPid), -    LoopData#rt{meta_tracer=undefined}; -do_stop_tracing_metatracing(LoopData) ->    % No meta tracer running! -    LoopData. - -%% Help function killing the autostarter, if one is active. -do_stop_tracing_kill_autostarter(P) when is_pid(P) -> -    exit(P,stop_tracing); -do_stop_tracing_kill_autostarter(_) ->      % No autostarter, do nothing. -    true. -%% ----------------------------------------------------------------------------- - -%% Help function implementing suspending the runtime component. -%% Returns a new loopdata structure. -do_suspend(LD,Reason) -> -    do_clear_trace_flags(),                 % If no process flags, no output! -    do_suspend_metatracer(LD#rt.meta_tracer), -    do_suspend_fetchers(LD#rt.fetchers), -    do_stop_tracing_kill_autostarter(LD#rt.auto_starter), -    NewLD=LD#rt{fetchers=[],status={suspended,Reason},auto_starter=undefined}, -    send_event(state_change,NewLD),         % Notify subscribers. -    NewLD. - -do_suspend_metatracer(MetaTracer) when is_pid(MetaTracer) -> -    inviso_rt_meta:suspend(MetaTracer);     % This makes it suspended. -do_suspend_metatracer(_) -> -    true. - -do_suspend_fetchers([FetcherPid|Rest]) -> -    FetcherPid ! {suspend,self()},          % This makes it terminate. -    do_suspend_fetchers(Rest); -do_suspend_fetchers([]) -> -    true. -%% ------------------------------------------------------------------------------ - -%% Function that stops all tracing, removes all trace-patterns and removes all -%% logfiles. The idea is to return the runtime component to the 'new' state. -do_clear(LoopData,Opts) when is_list(Opts) -> -    NewLoopData=do_stop_tracing(LoopData),  % First stop tracing, if tracing. -    case lists:member(keep_trace_patterns,Opts) of -	false -> -	    do_clear_trace_patterns(); -	_ -> -	    true -    end, -    case lists:member(keep_log_files,Opts) of -	false -> -	    if -		NewLoopData#rt.tracerdata/=undefined -> -		    do_delete_logs(NewLoopData#rt.tracerdata); -		true ->                     % If no tracerdata, nothing to remove! -		    true                    % Do nothing then. -	    end; -	_ -> -	    true -    end, -    NewLoopData#rt{state=new,tracerdata=undefined}; -do_clear(LoopData,_Opts) ->                 % Faulty Opts. -    do_clear(LoopData,[]).                  % Then just ignore the options. -%% ----------------------------------------------------------------------------- - -%% Function which takes a tracerdata, either our own or a "suggested" -%% and tries to find the corresponding files. Note that the return value only -%% contains "types" of logs that the tracerdata is pointing out. Hence -%% is there no ti-log, no one will be mentioned in the return value. -do_list_logs(TracerData) ->                   % Handles both list and tuple. -    case translate_td(TracerData) of -	{ok,LogTD,TiTD} -> -	    {TraceDir,TraceLogs}=list_logs_tracelog(LogTD), -	    {TiDir,TiLogs}=list_logs_tilog(TiTD), -	    case {TraceLogs,TiLogs} of -		{no_log,no_log} ->            % Tracerdata not generating logs! -		    {ok,no_log}; -		{_,no_log} ->                 % No ti logs. -		    {ok,[{trace_log,TraceDir,TraceLogs}]}; -		{no_log,_} ->                 % Only ti-logs, unusual! -		    {ok,[{ti_log,TiDir,TiLogs}]}; -		_ ->                          % Both trace and ti logs. -		    {ok,[{trace_log,TraceDir,TraceLogs},{ti_log,TiDir,TiLogs}]} -	    end; -	{error,Reason} -> -	    {error,Reason} -    end. -%% ----------------------------------------------------------------------------- - -%% Help function implementing fetching logfiles using distributed Erlang. -%% This function works for both situations, a list of specific files are -%% requested, or a tracerdata is specified. -%% Returns {Reply,NewLoopData}. -do_fetch_log(LD,CollectPid,What) -> -    if -	LD#rt.state/=tracing -> -	    case is_list_of_files_or_tracerdata(What) of -		files -> -		    FetcherPid=do_fetch_log_listoffiles(CollectPid,What), -		    {{ok,FetcherPid},add_fetcher_ld(FetcherPid,LD)}; -		tracerdata -> -		    case do_fetch_log_tracerdata(CollectPid,What) of -			{Reply,FetcherPid} when is_pid(FetcherPid) -> -			    {Reply,add_fetcher_ld(FetcherPid,LD)}; -			{Reply,_} ->        % No fetch process was started. -			    {Reply,LD} -		    end; -		false ->                    % It is an empty list! -		    {{complete,no_log},LD}; -		error ->                    % Incorrect parameter. -		    {{error,badarg},LD} -	    end; -	true ->                             % No transfere during tracing. -	    {{error,tracing},LD} -    end. - -%% Function taking tracerdata to find out what files to send over to the RemotePid. -%% Note that we will not go back to the loop function from here but rather call -%% the fetch_loop instead, prepresenting the fetch-log state. Unless we encounter -%% a problem. -do_fetch_log_tracerdata(CollectPid,TracerData) -> -    case do_list_logs(TracerData) of -	{ok,no_log} -> -	    {{complete,no_log},void}; -	{ok,Logs} ->                          % Ok, some trace_log and ti_log. -	    FetcherPid=do_fetch_log_listoffiles(CollectPid,Logs), -	    {{ok,FetcherPid},FetcherPid}; -	{error,Reason} ->                     % Problem with tracerdata! -	    {{error,Reason},void} -    end. - -do_fetch_log_listoffiles(CollectPid,FileSpec) -> -    ExpandedFileSpec=do_fetch_log_expand_filespec(FileSpec), -%% !!! try out different ChunkSizes -%	            ChunkSize = 60, -%	            ChunkSize = 7*1024, -    ChunkSize=1024, -    _Fetcher=spawn_link(?MODULE, -			fetch_init, -			[self(),ExpandedFileSpec,CollectPid,ChunkSize]). - -%% Help function which expands the list of logs to have tags in front of every -%% file, as required by the fetch_loop. -do_fetch_log_expand_filespec(Logs) -> -    TraceLogs= -	case lists:keysearch(trace_log,1,Logs) of -	    {value,{_,Dir1,Logs1}} ->         % There is a list of trace-logs. -		lists:map(fun(File)->{trace_log,Dir1,File} end,Logs1); -	    false ->                          % No trace-logs! -		[] -	end, -    TiLogs= -	case lists:keysearch(ti_log,1,Logs) of -	    {value,{_,Dir2,Logs2}} -> -		lists:map(fun(File)->{ti_log,Dir2,File} end,Logs2); -	    false -> -		[] -	end, -    TiLogs++TraceLogs. - -%% ------------------------------------------------------------------------------ - -%% Function that removes all logfiles associated with a certain tracerdata. -do_delete_logs(TracerDataOrLogList) -> -    case is_list_of_files_or_tracerdata(TracerDataOrLogList) of -	tracerdata -> -	    case translate_td(TracerDataOrLogList) of -		{ok,LogTD,TiTD} -> -		    case {list_logs_tracelog(LogTD),list_logs_tilog(TiTD)} of -			{{_,no_log},{_,no_log}} ->  % No logs nowhere! -			    {ok,no_log}; -			{{LogDir,LogFiles},{_,no_log}} -> % No ti. -			    {ok,[{trace_log,delete_files(LogDir,LogFiles)}]}; -			{{_,no_log},{TiDir,TiFiles}} -> -			    {ok,[{ti_log,delete_files(TiDir,TiFiles)}]}; -			{{LogDir,LogFiles},{TiDir,TiFiles}} -> -			    {ok,[{trace_log,delete_files(LogDir,LogFiles)}, -				 {ti_log,delete_files(TiDir,TiFiles)}]} -		    end; -		{error,Reason} -> -		    {error,Reason} -	    end; -	files ->                              % It is [{trace_log,Dir,Files},.. -	    if -		is_list(hd(TracerDataOrLogList)) -> % Just a list of files. -		    {ok,delete_files(".",TracerDataOrLogList)}; -		is_tuple(hd(TracerDataOrLogList)) -> % A "modern" logspec. -		    case {lists:keysearch(trace_log,1,TracerDataOrLogList), -			  lists:keysearch(ti_log,1,TracerDataOrLogList)} of -			{false,false} ->      % Hmm, no logs specified! -			    {ok,[]};          % Easy response! -			{{value,{_,LogDir,LogFiles}},false} -> -			    {ok,[{trace_log,delete_files(LogDir,LogFiles)}]}; -			{false,{value,{_,TiDir,TiFiles}}} -> -			    {ok,[{ti_log,delete_files(TiDir,TiFiles)}]}; -			{{value,{_,LogDir,LogFiles}},{value,{_,TiDir,TiFiles}}} -> -			    {ok,[{trace_log,delete_files(LogDir,LogFiles)}, -				 {ti_log,delete_files(TiDir,TiFiles)}]} -		    end -	    end; -	false ->                              % Can't tell which! -	    {ok,[]}; -	error -> -	    {error,{badarg,TracerDataOrLogList}} -    end. -%% ----------------------------------------------------------------------------- - -%% Function handling the request when a control component wishing to take -%% control over this already existing control component. It does not matter -%% what state it is in. It can very well already be tracing. -%% Returns {Reply,NewLoopData}. -%% Where the Reply tells the control component wether it took control of it -%% or not. {node_info,node(),self(),Vsn,State,Status,{tag,Tag}} means that we -%% can be adopted (and more precisely considers ourselves being adopted now). -do_try_to_adopt(Tag,if_ref,LoopData=#rt{tag=Tag},_Ctrl) -> -    {{error,{wrong_reference,LoopData#rt.tag}},LoopData}; -do_try_to_adopt(NewTag,_Condition,LoopData,CtrlPid) -> -    case LoopData#rt.timer_ref of           % Do we have a running-alone timer? -	undefined ->                        % No we don't. -	    true; -	TimerRef -> -	    timer:cancel(TimerRef) -    end, -    CtrlRef=erlang:monitor(process,CtrlPid), % Lets monitor our new "master"! -    {DepVal,_}=LoopData#rt.dependency, -    {node_info,Node,Pid,VSN,State,Status,Tag}=collect_node_info(LoopData), -    NewLoopData= -	LoopData#rt{dependency={DepVal,node(CtrlPid)}, -		    ctrl=CtrlPid, -		    ctrl_ref=CtrlRef,       % Monitoring our new master. -		    tag=NewTag,             % Use this tag from now on. -		    timer_ref=undefined}, -    {{node_info,Node,Pid,VSN,State,Status,{tag,Tag}},NewLoopData}. -%% ----------------------------------------------------------------------------- - -%% Function changing parameters accoring to a new options list. Note that we -%% can not change control component if the one we have is still working. -%% We can however of course change how this runtime component will react to -%% a running alone scenario. -%% Returns 'stop' or NewLoopData. -do_change_options(Options,LoopData) -> -    NewLoopData=read_option_list(Options,LoopData), -    if -	NewLoopData/=LoopData ->            % Some options changed. -	    case do_change_options_ctrl(LoopData,NewLoopData) of -		stop -> -		    stop; -		{ok,NewLoopData2} -> -		    NewLoopData3=do_change_options_overload(LoopData,NewLoopData2), -		    NewLoopData3#rt{next_loadcheck=now()} % Force a load check next. -	    end; -	true -> -	    LoopData -    end. - -%% Help function which sets up the new dependencies. Note that we only do that -%% if do not have a working control component. -%% Returns {ok,NewLoopData} or 'stop'. -do_change_options_ctrl(OldLD,NewLD) -> -    if -	OldLD#rt.timer_ref/=undefined ->    % No control and waiting to terminate. -	    timer:cancel(OldLD#rt.timer_ref), -	    do_down_message(NewLD#rt{timer_ref=undefined}); -	OldLD#rt.ctrl==undefiend ->         % No control component. -	    do_down_message(NewLD); -	true ->                             % We have a working control component! -	    {ok,NewLD} -    end. - -do_change_options_overload(OldLD,NewLD) -> -    if -	OldLD#rt.overload/=NewLD#rt.overload -> -	    terminate_overload(OldLD), -	    NewOverloadData=initialize_overload(NewLD), -	    NewLD#rt{overload_data=NewOverloadData}; -	true ->                             % No changes done. -	    NewLD -    end. -%% ----------------------------------------------------------------------------- - -%% Help function handling an incoming DOWN message from our control component. -%% If the runtime component is not allowed to run without a control component, it -%% simply terminates which closes the trace-port and process trace flags are -%% therefore automatically removed. -%% Returns 'stop' or a {ok,NewLoopData} structure. -do_down_message(LoopData) -> -    case LoopData#rt.dependency of -	{0,_} ->                            % Not allowed to run without controller. -	    stop; -	{infinity,_} ->                     % Don't care. Just remove the controller. -	    {ok,LoopData#rt{ctrl=undefined,ctrl_ref=undefined}}; -	{TimeOut,_} ->                      % Allowed to run TimeOut ms alone. -	    {ok,TimerRef}=timer:exit_after(TimeOut,self(),running_alone), -	    {ok,LoopData#rt{timer_ref=TimerRef,ctrl=undefined,ctrl_ref=undefined}} -    end. -%% ----------------------------------------------------------------------------- - -%% Function handling incomming exit signals. We can expect exit signals from the -%% following: Our parent supervisor (runtime_tools_sup), a meta-tracer process, -%% a logfile fetcher process, or the auto_starter. -%% A trace-port may also generate an exit signal. -%% In addition it is possible that an overload mechanism generates exit-signals. -%% We can also get the running_alone exit signal from our self. This is the -%% situation if our control component has terminated and this runtime component -%% is not allowed to exist on its own for ever. -%% Also note that after we have stopped tracing, for any reason, it is not -%% impossible that we receive the EXIT signals from still working parts that -%% we are now shuting down. This is no problem, the code will mearly update -%% the loopdata structure once again. -%% Returns 'exit' indicating that the runtime component shall terminate now, -%% {NewLoopData,NewTimeOut} if the exit-signal resulted in an overload check, or -%% a new loopdata structure shall we ignore the exit, or it simply resulted in -%% a state-change. -act_on_exit(Parent,_Reason,#rt{parent=Parent}) -> -    exit; -act_on_exit(_Pid,running_alone,_LoopData) -> -    exit; -act_on_exit(MetaTracer,_Reason,LoopData=#rt{meta_tracer=MetaTracer}) -> -    LoopData#rt{meta_tracer=undefined};     % It does not exit anylonger. -act_on_exit(Port,Reason,LoopData=#rt{tracer_port=Port}) ->  -    send_event({port_down,node(),Reason},LoopData), -    _NewLoopData=do_stop_tracing(LoopData); -act_on_exit(AutoStarter,_Reason,LoopData=#rt{auto_starter=AutoStarter}) -> -    LoopData#rt{auto_starter=undefined};    % The autostarter has terminated. -act_on_exit(Pid,Reason,LoopData) -> -    case remove_fetcher_ld(Pid,LoopData) of -	{true,NewLoopData} ->               % Yes it really was a fetcher. -	    NewLoopData; -	false ->                            % No it was not a fetcher. -	    act_on_exit_overload(Pid,Reason,LoopData) -    end. - -%% Help function checking if this exit has anything to do with an overload -%% mechanism. Note that here we run the overload mechanism regardless of -%% if we are tracing or not. This because an exit signal from the overload -%% must most likely always be handled. -act_on_exit_overload(Pid,Reason,LoopData) -> -    if -	LoopData#rt.overload/=?NO_LOADCHECK -> -	    {_NewLD,_NewTimeOut}= -		do_check_overload(LoopData, -				  {'EXIT',{Pid,Reason,LoopData#rt.overload_data}}); -	true ->                             % Overload not in use. -	    LoopData -    end. -%% ----------------------------------------------------------------------------- - - - - - - - - - - - - - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -%% ============================================================================== -%% Various help functions. -%% ============================================================================== - -%% Help function which calculates a new now-tuple by adding Interval milliseconds -%% to the first argument. Note that Interval may be 'infinity' too. -%% Returns a new now-tuple or "bigvalue" which is greater than any now-tuple. -add_to_now({MegSec,Sec,MicroSec},Interval) when is_integer(Interval) -> -    NewSec=Sec+(Interval div 1000), -    if -	NewSec>=1000000 -> -	    {MegSec+1,NewSec-1000000,MicroSec}; -	true -> -	    {MegSec,NewSec,MicroSec} -    end; -add_to_now(_,infinity) -> -    "bigvalue". -%% ------------------------------------------------------------------------------ - -%% Help function calculating the difference in milliseconds between its first -%% and second argument. This is useful when calculating an after timeout value -%% from current now() and next_loadcheck value. -calc_diff_to_now(T1={_,_,_},T2={_,_,_}) -> -    TimeOut1=timer:now_diff(T2,T1),          % The difference in microseconds. -    if -	TimeOut1<0 -> -	    0; -	true ->                              % Make milliseconds out of it. -	    TimeOut1 div 1000 -    end; -calc_diff_to_now(_T1,_) ->                   % Next loadcheck is not activated. -    infinity.                                % The the after timeout is infinity. -%% ------------------------------------------------------------------------------ - - -%% Help function returning information about this runtime component. -collect_node_info(#rt{vsn=VSN,state=State,status=Status,tag=Tag}) ->  -    {node_info,node(),self(),VSN,State,Status,Tag}. -%% ------------------------------------------------------------------------------ - -%% Help function sending information to the control component that state/status -%% change has occurred. Returns nothing significant. -send_event(state_change,LoopData=#rt{ctrl=CtrlPid}) when is_pid(CtrlPid) -> -    Event={trace_event,{state_change,node(),{LoopData#rt.state,LoopData#rt.status}}}, -    CtrlPid ! Event; -send_event(Event,#rt{ctrl=CtrlPid}) when is_pid(CtrlPid) -> -    CtrlPid ! {event,Event}; -send_event(_,_) ->                          % We have no control to send to! -    true.                                   % Maybe tracing alone after autostart. -%% ------------------------------------------------------------------------------ - -%% Help function initializing the overload protection mechanism. This may be -%% necessary if it is a port program or similar. Returns {ok,Data} or 'void'. -%% The datastructure vill be given to LoadMF as argument whenever loadchecks -%% are done. -initialize_overload(#rt{overload={_MF,_Interval,{M,F,Args},_RemoveMFA}}) -> -    case catch apply(M,F,Args) of -	{ok,Data} -> -	    Data; -	_ ->                                % 'EXIT' or other faulty returnvalue. -	    void -    end; -initialize_overload(_) -> -    void. -%% ------------------------------------------------------------------------------ - -%% Help function which terminates an overload protection mechanism. -%% Returns nothing significant. -terminate_overload(#rt{overload={_MF,_Interval,_InitMFA,{M,F,Args}}, -		       overload_data=Data}) -> -    catch apply(M,F,[Data|Args]),           % Interested in the side-effect. -    true; -terminate_overload(_) -> -    true. -%% ------------------------------------------------------------------------------ - - -%% Help function which checks that a process specified for trace flags is correct. -%% Either the built-in "aliases" for groups of processes, a pid, a locally registered -%% name. This function also works for globally registered names. It must then -%% first be established that the process is local for this node before setting any -%% process flags. -%% Returns {ok,PidSpec}, 'false' or {error,Reason}. -check_traceflag_pidspec(all) -> {ok,all}; -check_traceflag_pidspec(new) -> {ok,new}; -check_traceflag_pidspec(existing) -> {ok,existing}; -check_traceflag_pidspec(Name) when is_atom(Name) -> -    check_traceflag_pidspec({local,Name}); -check_traceflag_pidspec({local,A}) when is_atom(A) -> -    case whereis(A) of -	undefined ->                         % Then it is considered faulty. -	    {error,{nonexistent_name,A}}; -	Pid when is_pid(Pid) -> -	    {ok,Pid} -    end; -check_traceflag_pidspec({global,Name}) when is_atom(Name) -> -    case global:whereis_name(Name) of -	undefined ->                         % Then the name does not exist at all. -	    {error,{nonexistent_name,{global,Name}}}; -	Pid when is_pid(Pid) ->                 % Ok, but must check that it is here. -	    if -		node()==node(Pid) -> -		    {ok,Pid}; -		true ->                      % Pid is not at this node. -		    false                    % Not an error but cant be used. -	    end -    end; -check_traceflag_pidspec(Pid) when is_pid(Pid) -> -    {ok,Pid}; -check_traceflag_pidspec(Proc) -> -    {error,{faulty,Proc}}. -%% ------------------------------------------------------------------------------ - -%% Help function removing all trace flags from all processes. Useful in connection -%% with suspend. Returns nothing significant. -do_clear_trace_flags() -> -    erlang:trace(all, false, [all]). -%% ------------------------------------------------------------------------------ - -%% Help function which checks that only valid process trace flags are mentioned. -%% In order to create better fault reports. -%% Returns a list of the approved flags, or {error,Reason}. -check_flags(Flags) -> -    check_flags_2(Flags,Flags). - -check_flags_2([send|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2(['receive'|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2([call|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2([return_to|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2([procs|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2([garbage_collection|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2([running|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2([set_on_spawn|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2([set_on_first_spawn|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2([set_on_link|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2([timestamp|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2([arity|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2([silent|Rest],Flags) -> check_flags_2(Rest,Flags); -check_flags_2([],Flags) -> Flags; -check_flags_2([Faulty|_],_Flags) -> {error,{bad_flag,Faulty}}. -%% ------------------------------------------------------------------------------     - -%% Help function which checks parameters to erlang:trace_pattern. The purpose of -%% the function is to avoid to get multiple error return values in the return -%% list for a pattern used together with a regexp expanded module name. -check_pattern_parameters(Mod,Func,Arity,MS) -> -    MSresult = check_MS(MS), -    MFAresult = check_MFA(Mod,Func,Arity), -    MFAresult and MSresult. - -check_MS(MS) when is_list(MS) -> true; -check_MS(true) -> true; -check_MS(false) -> true. -     -check_MFA('_','_','_') -> true; -check_MFA(Mod,'_','_') when is_atom(Mod) -> true; -check_MFA(Mod,'_',A) when is_atom(Mod), is_integer(A) -> false; -check_MFA(Mod,F,'_') when is_atom(Mod), is_atom(F) -> true; -check_MFA(Mod,F,A) when is_atom(Mod), is_atom(F), is_integer(A) -> true. - -%% ----------------------------------------------------------------------------- - -%% Help function finding out if Mod is loaded, and if not, if it can successfully -%% be loaded. The Opts list can prevent modules from being loaded. -%% Returns 'true' or 'false'. -load_module_on_option(Mod,Opts) when is_list(Opts) -> -    case lists:member(no_loadcheck,Opts) of -	true ->                             % Then just skip this, return true. -	    true; -	false -> -	    case erlang:module_loaded(Mod) of -		true -> -		    true;                   % It is loaded, do no more. -		false -> -		    case lists:member(only_loaded,Opts) of -			true ->             % Then, make no attempts to load. -			    false; -			false ->            % Try to load! -			    case code:ensure_loaded(Mod) of -				{module,_Mod} -> % Successfully loaded! -				    true; -				{error,_Reason} -> -				    false -			    end -		    end -	    end -    end; -load_module_on_option(Mod,_Opts) ->        % Most likely Opts not a list! -    load_module_on_option(Mod,[]).         % Call without options. -%% ----------------------------------------------------------------------------- - -%% Help function taking a tuplelist of options turning them into a loopdata -%% structure. Returns the loopdata structure with the new values changed. -read_option_list([],LD) ->                  % Done, return loopdata. -    LD; -read_option_list([{dependency,{Value,Node}}|Rest],LD) -> -    read_option_list(Rest,LD#rt{dependency={Value,Node}}); -read_option_list([{dependency,Value}|Rest],LD) when is_integer(Value);Value==infinity -> -    read_option_list(Rest,LD#rt{dependency={Value,node()}}); -read_option_list([overload|Rest],LD) ->     % So that we can remove loadcheck. -    read_option_list(Rest,LD#rt{overload=?NO_LOADCHECK}); -read_option_list([{overload,{MF,Interval}}|Rest],LD) -  when is_integer(Interval);Interval==infinity -> -    read_option_list(Rest,LD#rt{overload={MF,Interval,void,void}}); -read_option_list([{overload,{MF,Interval,InitMFA,RemoveMFA}}|Rest],LD) -  when is_integer(Interval);Interval==infinity -> -    read_option_list(Rest,LD#rt{overload={MF,Interval,InitMFA,RemoveMFA}}); -read_option_list([{overload,Interval}|Rest],LD) -  when is_integer(Interval);Interval==infinity -> -    read_option_list(Rest,LD#rt{overload={fun ?DEFAULT_OVERLOAD_FUNC/1, -					  Interval, -					  void, -					  void}}); -read_option_list([_|Rest],LD) ->            % Unknown option. -    read_option_list(Rest,LD). -%% ----------------------------------------------------------------------------- - -%% Help function which returns the version number for the runtime_tools -%% application. Since it is called from within the runtime_tools application -%% we can be "sure" that it really exists. -get_application_vsn() -> -    {value,{_,_,VSN}}=lists:keysearch(runtime_tools,1,application:loaded_applications()), -    VSN. -%% ----------------------------------------------------------------------------- - -%% Help function that examines an argument to determine if it is a list of files -%% or tracerdata. This since they are both complex structures, looking alike. -%% Returns 'tracerdata', 'files', 'false' or 'error'. Error is returned if it -%% can not be decided which it is. -is_list_of_files_or_tracerdata(What) -> -    case inviso_rt_lib:is_tracerdata(What) of -	true -> -	    tracerdata; -	false -> -	    if -		What==[] -> -		    false; -		is_list(What),is_list(hd(What)) -> -		    files; -		is_list(What) -> -		    case lists:keysearch(trace_log,1,What) of -			{value,_} -> -			    files; -			false -> -			    case lists:keysearch(ti_log,1,What) of -				{value,_} -> -				    files; -				false -> -				    error    % Neither tracerdata nor list of files. -			    end -		    end; -		true -> -		    error -	    end -    end. -%% ------------------------------------------------------------------------------ - -%% Help function which removes all files in the ListOfFiles, assuming they -%% are located in Dir. -%% Returns a list of [{ok,FileName},...{error,Reason},...] -delete_files(Dir,ListOfFiles) -> -    delete_files_2(Dir,ListOfFiles, []). - -delete_files_2(Dir,[File|Tail],Reply) when is_list(Dir),is_list(File) -> -    case catch file:delete(filename:join(Dir,File)) of -	ok -> -	    delete_files_2(Dir,Tail,[{ok,File}|Reply]); -	{error,Posix} -> -	    delete_files_2(Dir,Tail,[{error,{Posix,File}}|Reply]); -	{'EXIT',_Reason} ->                  % Probably not proper string. -	    delete_files_2(Dir,Tail,[{error,{badarg,[Dir,File]}}|Reply]) -    end; -delete_files_2(Dir,[Faulty|Tail],Reply) -> -    delete_files_2(Dir,Tail,[{error,{badarg,[Dir,Faulty]}}|Reply]); -delete_files_2(_,[],Reply) -> -    Reply. -%% ----------------------------------------------------------------------------- - -%% Help function which lists all trace logs belonging to this tracerdata. -%% Note that this function operates on internal LogTD structures. -list_logs_tracelog({file,FileName}) when is_list(FileName) -> -    case file:read_file_info(FileName) of -	{ok,_} ->                            % The file exists. -	    {filename:dirname(FileName),[filename:basename(FileName)]}; -	_ ->                                 % The file does not exist -	    {filename:dirname(FileName),[]} -    end; -list_logs_tracelog({file,Wrap}) when is_tuple(Wrap),element(2,Wrap)==wrap -> -    case {element(1,Wrap),element(3,Wrap)} of -	{FileName,Tail} when is_list(FileName),is_list(Tail) -> -	    case catch {filename:dirname(FileName),list_wrapset(FileName,Tail)} of -		{'EXIT',_Reason} ->          % Garbage in either lists. -		    {"",no_log};             % Interpret as no log for tracerdata. -		Tuple -> -		    Tuple -	    end; -	_ -> -	    {"",no_log} -    end; -list_logs_tracelog(void) ->                  % Trace log not used. -    {"",no_log}; -list_logs_tracelog(_) ->                     % Some fun or similar. -    {"",no_log}.                             % Then there are no files to report. -%% ----------------------------------------------------------------------------- - -%% Help function which lists all ti-files belonging to this tracerdata. -%% Note that this function operates on the internal TiTD structure. -list_logs_tilog(TiTD) -  when tuple_size(TiTD)>=2,element(1,TiTD)==file,is_list(element(2,TiTD)) -> -    FileName=element(2,TiTD), -    case file:read_file_info(FileName) of -	{ok,_} ->                            % Yes the file exists. -	    {filename:dirname(FileName),[filename:basename(FileName)]}; -	_ -> -	    {filename:dirname(FileName),[]} -    end; -list_logs_tilog(void) ->                     % Internal representation for -    {"",no_log};                             % ti-file not in use. -list_logs_tilog(_) -> -    {"",no_log}. -%% ----------------------------------------------------------------------------- - -%% Help function which lists all files belonging to the wrap-set specified by -%% Prefix and Suffix. Note that there can be a directory in Prefix as well. -%% Will fail if either of Prefix or Suffix are not proper strings. -%% Returns a list of files, without dirname. -list_wrapset(Prefix,Suffix) -> -    Name=filename:basename(Prefix), -    Dirname=filename:dirname(Prefix), -    case file:list_dir(Dirname) of -	{ok,Files} -> -	    RegExp="^"++list_wrapset_escapes(Name)++"[0-9]+"++ -		list_wrapset_escapes(Suffix)++"$", -	    list_wrapset_2(Files,RegExp); -	{error,_Reason} ->                   % Translate this to no files! -	    [] -    end. - -list_wrapset_2([File|Rest],RegExp) -> -    Length=length(File), -    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) -    end; -list_wrapset_2([],_) -> -    []. - -%% Help function which inserts escape characters infront of characters which -%% will otherwise be missinterpreted by the regexp function as meta rather than -%% just the character itself. -list_wrapset_escapes([$.|Rest]) -> -    [$\\,$.|list_wrapset_escapes(Rest)]; -list_wrapset_escapes([Char|Rest]) -> -    [Char|list_wrapset_escapes(Rest)]; -list_wrapset_escapes([]) -> -    []. -%% ----------------------------------------------------------------------------- - - - - - - -%% ============================================================================== -%% Handler functions for implementing simple trace-message handlers. -%% ============================================================================== -%% -%% A handler must be a function taking two arguments. The first is the trace- -%% message. The second is datastructure used by the handler. The handler shall -%% returns (possibly) new datastructure. - -%% ------------------------------------------------------------------------------ -%% Function implementing a relayer. This function is used to creat a fun handler -%% if the relay option is used in tracer-data. -%% ------------------------------------------------------------------------------ -relay_handler(Msg,Tracer) -> -    Tracer ! Msg, -    Tracer. - -%% ------------------------------------------------------------------------------ -%% Function implementing a default terminal io handler. -%% ------------------------------------------------------------------------------ - -dhandler(end_of_trace, Out) -> -    Out; -dhandler(Trace, Out) when element(1, Trace) == trace,  -			  tuple_size(Trace) >= 3 -> -    dhandler1(Trace, tuple_size(Trace), Out); -dhandler(Trace, Out) when element(1, Trace) == trace_ts,  -			  tuple_size(Trace) >= 4 -> -    dhandler1(Trace, tuple_size(Trace)-1, Out); -dhandler(Trace, Out) when element(1, Trace) == drop,  -			  tuple_size(Trace) == 2 -> -    io:format(Out, "*** Dropped ~p messages.~n", [element(2,Trace)]), -    Out; -dhandler(Trace, Out) when element(1, Trace) == seq_trace,  -			  tuple_size(Trace) >= 3 -> -    SeqTraceInfo = case Trace of -		       {seq_trace, Lbl, STI, TS} -> -			   io:format(Out, "SeqTrace ~p [~p]: ", -				     [TS, Lbl]), -			   STI; -		       {seq_trace, Lbl, STI} -> -			   io:format(Out, "SeqTrace [~p]: ", -				     [Lbl]), -			   STI  -		   end, -    case SeqTraceInfo of -	{send, Ser, Fr, To, Mes} -> -	    io:format(Out, "(~p) ~p ! ~p [Serial: ~p]~n", -		      [Fr, To, Mes, Ser]); -	{'receive', Ser, Fr, To, Mes} -> -	    io:format(Out, "(~p) << ~p [Serial: ~p, From: ~p]~n", -		      [To, Mes, Ser, Fr]); -	{print, Ser, Fr, _, Info} -> -	    io:format(Out, "-> ~p [Serial: ~p, From: ~p]~n", -		      [Info, Ser, Fr]); -	Else -> -	    io:format(Out, "~p~n", [Else]) -    end, -    Out; -dhandler(_Trace, Out) -> -    Out. - -dhandler1(Trace, Size, Out) -> -%%%!    Self = self(), -    From = element(2, Trace), -    case element(3, Trace) of -	'receive' -> -	    case element(4, Trace) of -		{dbg,ok} -> ok; -		Message -> io:format(Out, "(~p) << ~p~n", [From,Message]) -	    end; -	'send' -> -	    Message = element(4, Trace), -	    case element(5, Trace) of -%%%! This causes messages to disappear when used by ttb (observer). Tests -%%%! so far show that there is no difference in results with dbg even if I -%%%! comment it out, so  I hope this is only some old code which isn't -%%%! needed anymore... /siri -%%%!		Self -> ok; -		To -> io:format(Out, "(~p) ~p ! ~p~n", [From,To,Message]) -	    end; -	call -> -	    case element(4, Trace) of -		MFA when Size == 5 -> -		    Message = element(5, Trace), -		    io:format(Out, "(~p) call ~s (~p)~n",  -			      [From,ffunc(MFA),Message]); -		MFA -> -		    io:format(Out, "(~p) call ~s~n", [From,ffunc(MFA)]) -	    end; -	return -> %% To be deleted... -	    case element(4, Trace) of -		MFA when Size == 5 -> -		    Ret = element(5, Trace), -		    io:format(Out, "(~p) old_ret ~s -> ~p~n",  -			      [From,ffunc(MFA),Ret]); -		MFA -> -		    io:format(Out, "(~p) old_ret ~s~n", [From,ffunc(MFA)]) -	    end; -	return_from -> -	    MFA = element(4, Trace), -	    Ret = element(5, Trace), -	    io:format(Out, "(~p) returned from ~s -> ~p~n",  -		      [From,ffunc(MFA),Ret]); -	return_to -> -	    MFA = element(4, Trace), -	    io:format(Out, "(~p) returning to ~s~n", [From,ffunc(MFA)]); -	spawn when Size == 5 -> -	    Pid = element(4, Trace), -	    MFA = element(5, Trace), -	    io:format(Out, "(~p) spawn ~p as ~s~n", [From,Pid,ffunc(MFA)]); -	Op -> -	    io:format(Out, "(~p) ~p ~s~n", [From,Op,ftup(Trace,4,Size)]) -    end, -    Out. - - -%%% These f* functions returns non-flat strings - -%% {M,F,[A1, A2, ..., AN]} -> "M:F(A1, A2, ..., AN)" -%% {M,F,A}                 -> "M:F/A" -ffunc({M,F,Argl}) when is_list(Argl) -> -    io_lib:format("~p:~p(~s)", [M, F, fargs(Argl)]); -ffunc({M,F,Arity}) -> -    io_lib:format("~p:~p/~p", [M,F,Arity]); -ffunc(X) -> io_lib:format("~p", [X]). - -%% Integer           -> "Integer" -%% [A1, A2, ..., AN] -> "A1, A2, ..., AN" -fargs(Arity) when is_integer(Arity) -> integer_to_list(Arity); -fargs([]) -> []; -fargs([A]) -> io_lib:format("~p", [A]);  %% last arg -fargs([A|Args]) -> [io_lib:format("~p,", [A]) | fargs(Args)]; -fargs(A) -> io_lib:format("~p", [A]). % last or only arg - -%% {A_1, A_2, ..., A_N} -> "A_Index A_Index+1 ... A_Size" -ftup(Trace, Index, Index) ->  -    io_lib:format("~p", [element(Index, Trace)]); -ftup(Trace, Index, Size) ->  -    [io_lib:format("~p ", [element(Index, Trace)])  -     | ftup(Trace, Index+1, Size)]. -%% ------------------------------------------------------------------------------ - -%% ============================================================================== -%% Functions handling the trace-port. Copied from dbg.erl -%% ============================================================================== - -trace_port_control(Port, flush) -> -    case trace_port_control(Port, $f, "") of -	{ok, [0]} -> ok; -	{ok, _}   -> {error, not_supported_by_trace_driver}; -	Other     -> Other -    end. - -trace_port_control(Port, Command, Arg) when is_port(Port)-> -    case catch port_control(Port, Command, Arg) of -	{'EXIT', _} -> {error, {no_trace_driver, node()}}; -	Result      -> Result -    end. - - -trace_port(file, {Filename, wrap, Tail}) -> -    trace_port(file, {Filename, wrap, Tail, 128*1024}); -trace_port(file, {Filename, wrap, Tail, WrapSize}) -> -    trace_port(file, {Filename, wrap, Tail, WrapSize, 8}); -trace_port(file, {Filename, wrap, Tail, WrapSize, WrapCnt}) -  when is_list(Tail),  -       is_integer(WrapSize), WrapSize >= 0, WrapSize < (1 bsl 32), -       is_integer(WrapCnt), WrapCnt >= 1, WrapCnt < (1 bsl 32) -> -   trace_port1(file, Filename, {wrap, Tail, WrapSize, WrapCnt, 0}); -trace_port(file, {Filename, wrap, Tail, {time, WrapTime}, WrapCnt}) -  when is_list(Tail),  -       is_integer(WrapTime), WrapTime >= 1, WrapTime < (1 bsl 32), -       is_integer(WrapCnt), WrapCnt >= 1, WrapCnt < (1 bsl 32) -> -    trace_port1(file, Filename, {wrap, Tail, 0, WrapCnt, WrapTime}); -trace_port(file, Filename) when is_list(Filename) -> -    trace_port1(file, Filename, nowrap); - -trace_port(ip, Portno) when is_integer(Portno) ->  -    trace_port(ip,{Portno,50}); - -trace_port(ip, {Portno, Qsiz}) when is_integer(Portno), is_integer(Qsiz) ->  -    fun() -> -	    Driver = "trace_ip_drv", -	    Dir1 = filename:join(code:priv_dir(runtime_tools), "lib"), -	    case catch erl_ddll:load_driver(Dir1, Driver) of -		ok -> -		    ok; -		_ -> -		    Dir2 = filename:join( -			     Dir1,  -			     erlang:system_info(system_architecture)), -		    catch erl_ddll:load_driver(Dir2, Driver) -	    end, -	    L = lists:flatten( -		  io_lib:format("~s ~p ~p 2", -				[Driver, Portno, Qsiz])), -	    open_port({spawn, L}, [eof]) -    end. - -trace_port1(file, Filename, Options) -> -    Driver = "trace_file_drv", -    fun() -> -	    Name = filename:absname(Filename),  -	    %% Absname is needed since the driver uses  -	    %% the supplied name without further investigations,  -	    %% and if the name is relative the resulting path  -	    %% might be too long which can cause a bus error -	    %% on vxworks instead of a nice error code return. -	    %% Also, the absname must be found inside the fun, -	    %% in case the actual node where the port shall be -	    %% started is on another node (or even another host) -	    {Wrap, Tail} = -		case Options of -		    {wrap, T, WrapSize, WrapCnt, WrapTime} -> -			{lists:flatten( -			   io_lib:format("w ~p ~p ~p ~p ",  -					 [WrapSize, WrapCnt, WrapTime,  -					  length(Name)])), -			 T}; -		    nowrap -> -			{"", ""} -		end, -	    Command = Driver ++ " " ++ Wrap ++ "n " ++ Name ++ Tail, -	    Dir1 = filename:join(code:priv_dir(runtime_tools), "lib"), -	    case catch erl_ddll:load_driver(Dir1, Driver) of -		ok -> -		    ok; -		_ -> -		    Dir2 = filename:join( -			     Dir1,  -			     erlang:system_info(system_architecture)), -		    catch erl_ddll:load_driver(Dir2, Driver) -	    end, -	    if 	element(1, Options) == wrap -> -		    %% Delete all files from any previous wrap log -		    Files = wrap_postsort(wrap_presort(Name, Tail)), -		    lists:foreach( -		      fun(N) -> file:delete(N) end, -		      Files); -		true -> ok -	    end, -	    open_port({spawn, Command}, [eof]) -    end. - -%% Find all possible wrap log files. -%% Returns: a list of sort converted filenames. -%% -%% The sort conversion is done by extracting the wrap sequence counter -%% from the filename, and calling wrap_encode/2. -wrap_presort(Filename, Tail) -> -    Name = filename:basename(Filename), -    Dirname = filename:dirname(Filename), -    case file:list_dir(Dirname) of -	{ok, Files} -> -	    lists:zf( -	      fun(N) -> -		      case match_front(N, Name) of -			  false -> -			      false; -			  X -> -			      case match_rear(X, Tail) of -				  false -> -				      false; -				  C -> % Counter -				      case match_0_9(C) of -					  true -> -					      {true,  -%						 filename:join(Dirname, N)} -					       wrap_encode( -						 filename:join(Dirname, N), -						 C)}; -					  false -> -					      false -				      end -			      end -		      end -	      end, -	      Files); -	_ -> -	    [] -    end. - -%% Extract the filenames from a list of sort converted ones. -wrap_postsort(Files) ->     -    lists:map(fun wrap_name/1, Files). - -wrap_encode(N, C) -> -    {list_to_integer(C), N}. - -wrap_name({_C, N}) -> -    N. - -%% Returns what is left of ListA when removing all matching -%% elements from ListB, or false if some element did not match, -%% or if ListA runs out of elements before ListB. -match_front(ListA, []) when is_list(ListA) -> -    ListA; -match_front([], ListB) when is_list(ListB) -> -    false; -match_front([Hd|TlA], [Hd|TlB]) -> -    match_front(TlA,TlB); -match_front([_HdA|_], [_HdB|_]) -> -    false. - -%% Reversed version of match_front/2 -match_rear(ListA, ListB) when is_list(ListA), is_list(ListB) -> -    case match_front(lists:reverse(ListA), lists:reverse(ListB)) of -	false -> -	    false; -	List -> -	    lists:reverse(List) -    end. - -%% Returns true if the non-empty list arguments contains all -%% characters $0 .. $9. -match_0_9([]) -> -    false; -match_0_9([H]) when is_integer(H), $0 =< H, H =< $9 -> -    true; -match_0_9([H|T]) when is_integer(H), $0 =< H, H =< $9 -> -    match_0_9(T); -match_0_9(L) when is_list(L) -> -    false. -%% ----------------------------------------------------------------------------- - - -%% ----------------------------------------------------------------------------- -%% Functions working on the tracerdata structure. -%% ----------------------------------------------------------------------------- - -%% Tracerdata is the structure which specifies to where tracing is logged at this -%% runtime component. It may now (and in the future specify) several things. -%% Currently it can consist of: -%% LogTD: specifying how trace-log data shall be handled. -%% TiTD : trace information, specifying how trace information shall be handled. -%% -%% Tracerdata may also contain quick or standard forms of LogTD and/or TiTD. -%% For instance if a standard handler-fun shall be used. The handler fun is not -%% part of the tracerdata but rather specified by a constant. - - -%% Help function that translates an input-tracerdata to useful internal formats. -%% This since the tracerdata may consist of specifications which shall be -%% translated into funs or similar. -%% Returns {ok,LogTD,TiTD} or {error,Reason}. -%% Note that TiTD may be 'void' since TiTD is not mandatory. -translate_td(TracerData) when is_list(TracerData) -> % Both log and ti. -    case translate_td_logtd(get_trace_log_tracerdata(TracerData)) of -	{ok,LogTD} -> -	    case translate_td_titd(get_ti_log_tracerdata(TracerData)) of -		{ok,TiTD} -> -		    {ok,LogTD,TiTD}; -		{error,Reason} -> -		    {error,Reason} -	    end; -	{error,Reason} -> -	    {error,Reason} -    end; -translate_td(TracerData) ->                  % The it is just LogTD!? -    case translate_td_logtd(TracerData) of -	{ok,LogTD} -> -	    {ok,LogTD,void}; -	{error,Reason} -> -	    {error,Reason} -    end. -%% ----------------------------------------------------------------------------- - -%% Help function translating trace-log tracerdata. -translate_td_logtd(collector) ->             % This rt will act as receiver. -    {ok,{fun dhandler/2,user}};              % Simple terminal io. -translate_td_logtd({relayer,Tracer}) when is_pid(Tracer) -> -    {ok,{fun relay_handler/2,Tracer}};       % Relay trace-msg to Tracer-pid. -translate_td_logtd({HandlerFun,Data}) when is_function(HandlerFun) -> -    {ok,{HandlerFun,Data}};                  % Own invented fun. -translate_td_logtd({Type,Parameters}) when Type==ip;Type==file -> -    {ok,{Type,Parameters}};                  % Built in trace-port -translate_td_logtd(false) ->                 % Unusual but no trace log. -    {ok,void}; -translate_td_logtd(Arg) -> -    {error,{bad_log_td,Arg}}. -%% ----------------------------------------------------------------------------- - -%% Help function translating ti-log tracerdata. -translate_td_titd(TiTD={file,FileName}) when is_list(FileName) -> -    {ok,TiTD}; -translate_td_titd({file,FileName, -		   {InitPublLDmfa={M1,F1,L1}, -		    RemovePublLDmf={M2,F2}, -		    CleanPublLDmf={M3,F3}}}) -  when is_list(FileName),is_atom(M1),is_atom(F1),is_atom(M2),is_atom(F2),is_list(L1),is_atom(M3),is_atom(F3) -> -    {ok,{file,FileName,{InitPublLDmfa,RemovePublLDmf,CleanPublLDmf}}}; -translate_td_titd({file,FileName, -		   {InitPublLDmfa={M1,F1,L1}, -		    void, -		    CleanPublLDmf={M3,F3}}}) -  when is_list(FileName),is_atom(M1),is_atom(F1),is_list(L1),is_atom(M3),is_atom(F3) -> -    {ok,{file,FileName,{InitPublLDmfa,void,CleanPublLDmf}}}; -translate_td_titd(false) ->                  % Means no ti-tracerdata. -    {ok,void}; -translate_td_titd(TiTD) -> -    {error,{bad_ti_td,TiTD}}. -%% ----------------------------------------------------------------------------- - -%% This function retrieves the trace-log part of a TracerData list structure. -%% Returns TraceLogTD or 'false'. -get_trace_log_tracerdata(TracerData) -> -    case lists:keysearch(trace,1,TracerData) of -	{value,{_,LogTD}} -> -	    LogTD; -	false -> -	    false -    end. -%% ----------------------------------------------------------------------------- - -%% This function retrieves the ti-log part of a TracerData list structure. -%% Returns TiLogTD or 'false'. -get_ti_log_tracerdata(TracerData) -> -    case lists:keysearch(ti,1,TracerData) of -	{value,{_,TiTD}} -> -	    TiTD; -	false -> -	    false -    end. -%% ----------------------------------------------------------------------------- - -%% Help function which checks that parameters to the built in trace-port are -%% sane. -check_traceport_parameters(Type,Args) -> -    case {Type,Args} of -	{file,{FileName,wrap,Tail}} when is_list(FileName),is_list(Tail) -> -	    ok; -	{file,{FileName,wrap,Tail,WrapSize}} -	when is_list(FileName), -	is_list(Tail), -	is_integer(WrapSize),WrapSize>=0,WrapSize< (1 bsl 32) -> -	    ok; -	{file,{FileName,wrap,Tail,WrapSize,WrapCnt}} -	when is_list(FileName),is_list(Tail),  -	is_integer(WrapSize), WrapSize >= 0, WrapSize < (1 bsl 32), -	is_integer(WrapCnt), WrapCnt >= 1, WrapCnt < (1 bsl 32) -> -	    ok; -	{file,{FileName,wrap,Tail,{time,WrapTime},WrapCnt}} -	when is_list(FileName),is_list(Tail),  -	is_integer(WrapTime), WrapTime >= 1, WrapTime < (1 bsl 32), -	is_integer(WrapCnt), WrapCnt >= 1, WrapCnt < (1 bsl 32) -> -	    ok; -	{file,FileName} when is_list(FileName) -> -	    ok; -	{ip,Portno} when is_integer(Portno),Portno=<16#FFFF ->  -	    ok; -	{ip,{Portno,Qsiz}} when is_integer(Portno),Portno=<16#FFFF,is_integer(Qsiz) -> -	    ok; -	_ -> -	    {error,{trace_port_args,[Type,Args]}} -    end. -%% ----------------------------------------------------------------------------- - - -%% ----------------------------------------------------------------------------- -%% Default overload functionality. -%% ----------------------------------------------------------------------------- - -%% A default overload protection function. An overload function must take -%% one argument and return 'ok' or {suspend,SuspendReason}. -default_overload_func(_) -> -    case process_info(self(),message_queue_len) of -	{message_queue_len,N} when N > 1000 ->  -	    {suspend,rt_max_queue_len}; -	_ -> -	    ok -    end. -%% ----------------------------------------------------------------------------- - -%% ============================================================================= -%% Functions working on the internal loopdata structure. -%% ============================================================================= - -%% Help function simply adding Fetcher as a fetcher process to the loopdata. -%% Returns a new loopdata structure. -add_fetcher_ld(Fetcher,LD) -> -    LD#rt{fetchers=[Fetcher|LD#rt.fetchers]}. -%% ----------------------------------------------------------------------------- - -%% Help function investigating if the first argument is a known fetcher process -%% or not. If it is, it also removed it from the fetchers list in the loopdata -%% structure. -%% Returns {true,NewLoopData} or 'false'. -remove_fetcher_ld(Fetcher,LD) -> -    NewFetchers=lists:delete(Fetcher,LD#rt.fetchers), -    if -	NewFetchers/=LD#rt.fetchers -> -	    {true,LD#rt{fetchers=NewFetchers}}; -	true ->                             % No it was not a fetcher process. -	    false -    end. -%% ----------------------------------------------------------------------------- - -%%% end of file - diff --git a/lib/runtime_tools/src/inviso_rt_lib.erl b/lib/runtime_tools/src/inviso_rt_lib.erl deleted file mode 100644 index 5dfe14068a..0000000000 --- a/lib/runtime_tools/src/inviso_rt_lib.erl +++ /dev/null @@ -1,474 +0,0 @@ -%% -%% %CopyrightBegin% -%%  -%% Copyright Ericsson AB 2005-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 -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%%  -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%%  -%% %CopyrightEnd% -%% -%% ------------------------------------------------------------------------------ -%% File    : inviso_rt_lib.erl -%% Author  : Lennart �hman <[email protected]> -%% Description :  -%% -%% Created : 27 Sep 2005 by Lennart �hman <[email protected]> -%% ------------------------------------------------------------------------------ --module(inviso_rt_lib). - --export([expand_regexp/2,expand_regexp/3,expand_regexp/4]). --export([is_tracerdata/1]). --export([transform/2]). - --export([rpc/4,rpc/5,match_modules/2,match_modules/3]). --export([debug/3]). - -%% ------------------------------------------------------------------------------ - -%% ============================================================================== -%% Exported API functions. -%% ============================================================================== - -%% ------------------------------------------------------------------------------ -%% expand_regexp(Nodes,RegExpDir,RegExpMod,Opts) = [{Node,Answer},...] | {error,Reason} -%% expand_regexp(Nodes,RegExpMod,Opts) = [{Node,Answer},...] | {error,Reason} -%% expand_regexp(RegExpDir,RegExpMod,Opts) = ListOfModules | {error,Reason} -%% expand_regexp(RegExpMod,Opts) = ListOfModules | {error,Reason} -%%   Nodes=List of all nodes (atoms) where to expand. -%%   RegExpDir=Reg.exp (string) specifying directories. -%%   RegExpMod=Reg.exp (string) specifying module names. -%%   Node=node name (atom). -%%   Opts=[Opt,...] -%%   Opt=only_loaded -%%   Answer=List of modules (atoms) | 'badrpc' -%% -%% Expands, concurrently, the regular expression on Nodes and returns a list -%% of what modules it expanded to on the different nodes. Note that it may -%% differ between Erlang nodes depending on whether the modules are the same -%% or not. Also note that all modules becomes loaded as a result. -%% RegExpDir can further limit the modules. It introduces the requirement that -%% a module must be loaded from a directory with a path satisfying the RegExpDir. -%% All regular expression are according to the standard lib regexp module. -expand_regexp(RegExpMod,Opts) when is_list(RegExpMod),is_list(Opts) -> -    match_modules(RegExpMod,Opts); -expand_regexp(RegExpMod,Opts) -> -    {error,{badarg,[RegExpMod,Opts]}}. -expand_regexp(NodesOrRegExpDir,RegExpMod,Opts) -  when is_list(NodesOrRegExpDir),is_list(RegExpMod),is_list(Opts) -> -    case is_list_of_atoms(NodesOrRegExpDir) of -	true ->                              % Interpret as list of nodes. -	    lists:foreach(fun(N)->spawn(?MODULE,rpc,[self(),N,RegExpMod,Opts]) end, -			  NodesOrRegExpDir), -	    expand_regexp_answers(NodesOrRegExpDir,[]); -	false ->                             % Interpret as a string. -	    match_modules(NodesOrRegExpDir,RegExpMod,Opts) -    end; -expand_regexp(NodesOrRegExpDir,RegExpMod,Opts) -> -    {error,{badarg,[NodesOrRegExpDir,RegExpMod,Opts]}}. -expand_regexp(Nodes,RegExpDir,RegExpMod,Opts) -  when is_list(Nodes),is_list(RegExpDir),is_list(RegExpMod),is_list(Opts) -> -    lists:foreach(fun(N)-> -			  spawn(?MODULE,rpc,[self(),N,RegExpDir,RegExpMod,Opts]) -		  end, -		  Nodes), -    expand_regexp_answers(Nodes,[]); -expand_regexp(Nodes,RegExpDir,RegExpMod,Opts) -> -    {error,{badarg,[Nodes,RegExpDir,RegExpMod,Opts]}}. - -expand_regexp_answers([],Answers) -> Answers;  % List of [{Node,Answer},...]. -expand_regexp_answers(Nodes,Answers) -> -    receive -	{?MODULE,Node,Answer} -> -	    expand_regexp_answers(lists:delete(Node,Nodes),[{Node,Answer}|Answers]) -    end. -%% ------------------------------------------------------------------------------ - -%% is_tracerdata(TracerData)=true|false -%% Answers the question if TracerData is proper tracerdata. Note that true can be -%% returned if it resembles tracerdata very closely. -is_tracerdata({Fun,_Data}) when is_function(Fun) -> true; -is_tracerdata({relayer,To}) when is_pid(To);is_atom(To) -> true; -is_tracerdata(collector) -> true; -is_tracerdata({file,Param}) when is_tuple(Param);is_list(Param) -> true; -is_tracerdata({ip,_Param}) -> true; -is_tracerdata([{trace,LogTD}|Rest]) -> -    case is_tracerdata(LogTD) of -	true -> -	    is_tracerdata(Rest); -	false -> -	    false -    end; -is_tracerdata([{ti,TiData}|Rest]) -> -    case is_tidata(TiData) of -	true -> -	    is_tracerdata(Rest); -	false -> -	    false -    end; -is_tracerdata([]) -> -    true; -is_tracerdata(_) -> -    false. - -is_tidata({file,FileName}) when is_list(FileName) -> true;  -is_tidata({file,FileName,{M,F,Args}}) when is_list(FileName),is_atom(M),is_atom(F),is_list(Args) -> -    true; -is_tidata(_) -> false. -%% ------------------------------------------------------------------------------		 - - -%% ============================================================================== -%% Help functions. -%% ============================================================================== - -%% Help function intended to be run in its own process. Will report with -%% a message when done. -%% This function will be spawned on. -rpc(Parent,Node,RegExpMod,Opts) ->			    -    case rpc:call(Node,?MODULE,match_modules,[RegExpMod,Opts]) of -	{badrpc,_Reason} ->                  % The node is probably not healthy. -	    Parent ! {?MODULE,Node,badrpc}; -	Modules -> -	    Parent ! {?MODULE,Node,Modules} -    end. - -rpc(Parent,Node,RegExpDir,RegExpMod,Opts) ->			    -    case rpc:call(Node,?MODULE,match_modules,[RegExpDir,RegExpMod,Opts]) of -	{badrpc,_Reason} ->                  % The node is probably not healthy. -	    Parent ! {?MODULE,Node,badrpc}; -	Modules -> -	    Parent ! {?MODULE,Node,Modules} -    end. -%% ------------------------------------------------------------------------------ - - -%% ============================================================================== -%% Exported function which actually shall be in code.erl. -%% ============================================================================== - -%% match_modules(RegExpMod,Actions) = [Module,...] | {error,Reason} -%% match_modules(RegExpDir,RegExpMod,Actions)=[Module,...] | {error,Reason} -%%   RegExpMod=Erlang regular expression describing module names (string). -%%   RegExpDir=Erlang regular expression describing directory paths(string) | -%%             void -%%   Actions=List of;'only_loaded'. -%% -%% Function which matches a regular expresion against module names. The function -%% can also match the directory from where the module is loaded or will be loaded -%% against a regular expresion for directory paths. -%% The function uses the same strategy as code-loading if the same module is -%% discovered in several places. -%% (1) An already loaded module shadows all other occurancies. -%% (2) .beams found in by a path shadows .beams found by paths later in the -%%   code paths. -%% -%% Description of actions: -%% only_loaded: Only consider modules which are loaded. -match_modules(RegExpMod,Actions) -> -    match_modules(void,RegExpMod,Actions). -match_modules(RegExpDir,RegExpMod,Actions) -> -    AllLoaded=code:all_loaded(), -    Mods1=handle_expand_regexp_2(AllLoaded,RegExpDir,RegExpMod,[]), -    case lists:member(only_loaded,Actions) of % Shall we do not loaded too? -	false ->                             % Ok, search all paths too then. -	    Paths=code:get_path(), -	    handle_expand_regexp_3(Paths,RegExpDir,RegExpMod,AllLoaded,Mods1); -	true ->                              % Only loaded modules then. -	    Mods1 -    end. - - -%% Help function which traverses all loaded modules and determines -%% which shall be returned. First we check that the module satisfies the -%% module-regexp. Then we, if a dir reg-exp is given, checks that the -%% module is loaded from an approved path. Note that if it can not be -%% determined from where it was loaded (like preloaded or cover-compiled -%% etc), but dir reg-exps are used. That module will be excluded. -%% Returns a list of modules. -handle_expand_regexp_2([{Mod,Path}|Rest],RegExpDir,RegExpMod,Result) -> -    ModStr=atom_to_list(Mod), -    ModLen=length(ModStr), -    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 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) -		    end; -		true ->                      % Otherwise already done! -		    handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,[Mod|Result]) -	    end; -	_ ->                                 % Then Mod is not part of the set. -	    handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result) -    end; -handle_expand_regexp_2([],_,_,Result) -> Result. - -%% Help function which traverses all paths and looks for modules satisfying -%% the module reg.exp. -%% Returns a list of unique module names. -handle_expand_regexp_3([Path|Rest],RegExpDir,RegExpMod,AllLoaded,Result) -> -    if -	is_list(RegExpDir) ->                   % We must consider the directory name. -	    AbsPath= -		case filename:pathtype(Path) of -		    absolute ->              % Is already abs. -			Path; -		    relative ->              % Then it must be made absolute. -			filename:absname(Path); -		    volumerelative ->        % Only on Windows!? -			filename:absname(Path) -		end, -	    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. -		    handle_expand_regexp_3(Rest,RegExpDir,RegExpMod,AllLoaded,Result) -	    end; -	true ->                              % RegExpDir is not used! -	    NewResult=handle_expand_regexp_3_1(Path,RegExpMod,AllLoaded,Result), -	    handle_expand_regexp_3(Rest,RegExpDir,RegExpMod,AllLoaded,NewResult) -    end; -handle_expand_regexp_3([],_,_,_,Result) -> Result. - -handle_expand_regexp_3_1(Path,RegExpMod,AllLoaded,Result) -> -    case file:list_dir(Path) of -	{ok,FileNames} -> -	    handle_expand_regexp_3_2(FileNames,RegExpMod,AllLoaded,Result); -	{error,_Reason} ->                   % Bad path!? Skip it. -	    Result -    end. - -handle_expand_regexp_3_2([File|Rest],RegExpMod,AllLoaded,Result) -> -    case filename:extension(File) of -	".beam" ->                           % It is a beam-file. Consider it! -	    ModStr=filename:basename(File,".beam"), -	    Mod=list_to_atom(ModStr), -	    case {lists:keysearch(Mod,1,AllLoaded),lists:member(Mod,Result)} of -		{false,false} ->             % This module is not tried before. -		    ModLen=length(ModStr), -		    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) -		    end; -		{_,_} ->                     % This module is already tested. -		    handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,Result) -	    end; -	_ ->                                 % Not a beam-file, skip it. -	    handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,Result) -    end; -handle_expand_regexp_3_2([],_,_,Result) -> Result. -%% ------------------------------------------------------------------------------ - -%% Help function which finds out if its argument is a list of zero or more -%% atoms. -%% Returns 'true' or 'false'. -is_list_of_atoms([A|Rest]) when is_atom(A) -> -    is_list_of_atoms(Rest); -is_list_of_atoms([_|_]) -> -    false; -is_list_of_atoms([]) -> -    true. -%% ------------------------------------------------------------------------------ - - -%% ============================================================================= -%% Functions transforming function calls in trace-case file. -%% ============================================================================= - -%% transform(Exprs,Translations)=NewExprs -%%   Exprs=list(); List of abstract format erlang terms, as returned by -%%     io:parse_erl_exprs/2. -%%   Translations=list(); List of translations from function calls to other -%%     function calls. [{Mod,Func,Arity,{NewMod,NewFunc,ParamTransformMF}},...] -%%     Mod can actually be omitted, ParamTransformMF shall be {M,F} where F is -%%     a function taking one argument (the parameter list), and returning the -%%     new parameter list. It can also be anything else should no transformation -%%     of the parameters be the case. -%% -%% Function that transforms function calls in a trace-case file. The transform/2 -%% can only transform shallow function calls. I.e where both module and function -%% name are specified as atoms. Any binding-environment is not examined. -transform([Expr|Rest],Translations) -> -    [transform_2(Expr,Translations)|transform(Rest,Translations)]; -transform([],_) -> -    []. - -%% Help function handling a single expr. -transform_2({call,L1,{remote,L2,ModExpr,FuncExpr},Params},Translations) -> -    case transform_2(ModExpr,Translations) of -	{atom,L3,M} -> -	    case transform_2(FuncExpr,Translations) of -		{atom,L4,F} ->               % Now we have a M:F/Arity! -		    case do_call_translation(M,F,Params,Translations) of -			{ok,NewM,NewF,NewP} -> -			    NewParams=transform(NewP,Translations), -			    {call,L1,{remote,L2,{atom,L3,NewM},{atom,L4,NewF}},NewParams}; -			false ->             % No translation or faulty. -			    NewParams=transform(Params,Translations), -			    {call,L1,{remote,L2,ModExpr,FuncExpr},NewParams} -		    end; -		NewFuncExpr ->               % Not translated to a shallow term. -		    NewParams=transform(Params,Translations), -		    {call,L1,{remote,L2,ModExpr,NewFuncExpr},NewParams} -	    end; -	NewModExpr ->                        % Not translated to a shallow term. -	    NewFuncExpr=transform_2(FuncExpr,Translations), -	    NewParams=transform(Params,Translations), -	    {call,L1,{remote,L2,NewModExpr,NewFuncExpr},NewParams} -    end; -transform_2({call,L1,FuncExpr,Params},Translations) -> -    case transform_2(FuncExpr,Translations) of -	{atom,L3,F} ->                       % Now we have a M:F/Arity! -	    case do_call_translation(F,Params,Translations) of -		{ok,NewM,NewF,NewP} ->       % It is turned into a global call. -		    NewParams=transform(NewP,Translations), -		    {call,L1,{remote,L1,{atom,L3,NewM},{atom,L3,NewF}},NewParams}; -		false ->                     % No translation or faulty. -		    NewParams=transform(Params,Translations), -		    {call,L1,FuncExpr,NewParams} -	    end; -	NewFuncExpr ->                       % Not translated to a shallow term. -	    NewParams=transform(Params,Translations), -	    {call,L1,NewFuncExpr,NewParams} -    end; -transform_2({match,L,P,E},Translations) -> -    NewPattern=transform_2(P,Translations), -    NewExpr=transform_2(E,Translations), -    {match,L,NewPattern,NewExpr}; -transform_2({op,L,Op,Arg1,Arg2},Translations) -> -    NewArg1=transform_2(Arg1,Translations), -    NewArg2=transform_2(Arg2,Translations), -    {op,L,Op,NewArg1,NewArg2}; -transform_2({op,L,Op,Arg},Translations) -> -    NewArg=transform_2(Arg,Translations), -    {op,L,Op,NewArg}; -transform_2({block,L,Body},Translations) -> -    NewBody=transform(Body,Translations), -    {block,L,NewBody}; -transform_2({'if',L,Clauses},Translations) -> -    NewClauses=transform_clauses(Clauses,Translations), -    {'if',L,NewClauses}; -transform_2({'case',L,Func,Clauses},Translations) -> -    NewFunc=transform_2(Func,Translations), -    NewClauses=transform_clauses(Clauses,Translations), -    {'case',L,NewFunc,NewClauses}; -transform_2({'fun',L,{clauses,Clauses}},Translations) -> -    NewClauses=transform_clauses(Clauses,Translations), -    {'fun',L,NewClauses}; -transform_2({lc,L,Items,GeneratorsFilters},Translations) -> -    NewItem=transform_2(Items,Translations), -    NewGensAndFilters=transform_gensandfilters(GeneratorsFilters,Translations), -    {lc,L,NewItem,NewGensAndFilters}; -transform_2({'catch',L,Expr},Translations) -> -    NewExpr=transform_2(Expr,Translations), -    {'catch',L,NewExpr}; -transform_2({tuple,L,Elements},Translations) -> -    NewElements=transform(Elements,Translations), -    {tuple,L,NewElements}; -transform_2({cons,L,Element,Tail},Translations) -> -    NewElement=transform_2(Element,Translations), -    NewTail=transform_2(Tail,Translations), -    {cons,L,NewElement,NewTail}; -transform_2({nil,L},_) -> -    {nil,L}; -transform_2({bin,L,Elements},Translations) -> -    NewElements=transform_binary(Elements,Translations), -    {bin,L,NewElements}; -transform_2(Expr,_) ->                       % Can be a var for instance. -    Expr. - -transform_binary([{bin_element,L,Val,Size,TSL}|Rest],Translations) -> -    NewVal=transform_2(Val,Translations), -    NewSize=transform_2(Size,Translations), -    [{bin_element,L,NewVal,NewSize,TSL}|transform_binary(Rest,Translations)]; -transform_binary([],_) -> -    []. - -transform_clauses([{clause,L,Pattern,Guards,Body}|Rest],Translations) -> -    NewPattern=transform(Pattern,Translations), -    NewBody=transform(Body,Translations), -    [{clause,L,NewPattern,Guards,NewBody}|transform_clauses(Rest,Translations)]; -transform_clauses([],_Translations) -> -    []. - -transform_gensandfilters([{generator,L,Pattern,Exprs}|Rest],Translations) -> -    NewExprs=transform(Exprs,Translations), -    [{generator,L,Pattern,NewExprs}|transform_gensandfilters(Rest,Translations)]; -transform_gensandfilters([Expr|Rest],Translations) -> -    [transform_2(Expr,Translations)|transform_gensandfilters(Rest,Translations)]; -transform_gensandfilters([],_) -> -    []. -%% ------------------------------------------------------------------------------ - -%% This is the heart of the translation functionality. Here we actually try to -%% replace calls to certain functions with other calls. This can include removing -%% arguments. -do_call_translation(M,F,Params,Translations) -> -    case lists:keysearch({M,F,length(Params)},1,Translations) of -	{value,{_,{NewM,NewF,ArgFun}}} ->    % Lets transform the function. -	    do_call_translation_2(Params,NewM,NewF,ArgFun); -	_ -> -	    false                            % No translations at all. -    end. -do_call_translation(F,Params,Translations) -> -    case lists:keysearch({F,length(Params)},1,Translations) of -	{value,{_,{NewM,NewF,ArgFun}}} ->    % Lets transform the function. -	    do_call_translation_2(Params,NewM,NewF,ArgFun); -	_ -> -	    false                            % No translations at all. -    end. - -do_call_translation_2(Params,NewM,NewF,ArgFun) -> -    case ArgFun of -	{M,F} when is_atom(M),is_atom(F) -> -	    case catch M:F(Params) of -		{'EXIT',_Reason} -> -		    false;                   % If it does not work, skipp it. -		MungedParams when is_list(MungedParams) -> -		    {ok,NewM,NewF,MungedParams}; -		_ -> -		    false -	    end; -	_ ->                                 % No munging of parameters. -	    {ok,NewM,NewF,Params} -    end. -%% ------------------------------------------------------------------------------ - - -%% ============================================================================= -%% Functions for the runtime component internal debugging system. -%% ============================================================================= - -%% The debug system is meant to provide tracing of ttb at different levels. -%% -%% debug(What,Level,Description) -> nothing significant. -%%   What : controls what kind of event. This can both be certain parts of ttb -%%          as well as certain levels (info to catastrophy). -%%   Level: Determines if What shall be printed or not. -%%   Description: this is what happend. -debug(off,_What,_Description) -> -    true;                                    % Debug is off, no action. -debug(On,What,Description) -> -    debug_2(On,What,Description). - -debug_2(_,What,Description) -> -    io:format("INVISO DEBUG:~w, ~p~n",[What,Description]). -%% ----------------------------------------------------------------------------- diff --git a/lib/runtime_tools/src/inviso_rt_meta.erl b/lib/runtime_tools/src/inviso_rt_meta.erl deleted file mode 100644 index 6865dc2242..0000000000 --- a/lib/runtime_tools/src/inviso_rt_meta.erl +++ /dev/null @@ -1,1207 +0,0 @@ -%% -%% %CopyrightBegin% -%%  -%% Copyright Ericsson AB 2006-2009. 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 -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%%  -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%%  -%% %CopyrightEnd% -%% -%% Author: Lennart �hman, [email protected] -%% -%% This module implements the meta tracer process belonging to the -%% runtime component. Its main purpose is to write the ti-file (traceinformation). -%% The ti-file contains translations between process id:s and what ever "you" -%% want to read in the merged and formatted logfile. -%% This process interacts with the runtime component process. -%% -%% Currently it handles the following types of ti-files: -%%   Plain raw, binary log. -%%   Relay to other inviso_rt_meta process on another node. -%% -%% The TI file will be on binary format and each entry is: -%%   <<LengthIndicator:32, {Pid,Alias,Op,NowStamp} >> -%%       Pid=pid(), or if OP==unalias pid()|any_other_than_pid() -%%       Op=alias|unalias -%% ----------------------------------------------------------------------------- --module(inviso_rt_meta). - -%% ----------------------------------------------------------------------------- -%% API exports. -%% ----------------------------------------------------------------------------- - --export([start/2,start/5]). --export([stop/1,suspend/1]). --export([init_tpm/5,init_tpm/8]). --export([tpm/5,tpm/6,tpm/9,tpm_tracer/5,tpm_tracer/6,tpm_tracer/9]). --export([tpm_ms/6,tpm_ms_tracer/6,ctpm_ms/5,ctpm/4]). --export([local_register/1,global_register/1]). --export([remove_local_register/1,remove_global_register/1]). - --export([write_ti/1]). - --export([get_tracer/0,tpm_ms/5,tpm_ms_tracer/5,list_tpm_ms/3,ctpm_ms/4]). - --export([metacast_call/5,metacast_return_from/6]). --export([get_state/1]). -%% ----------------------------------------------------------------------------- - -%% ----------------------------------------------------------------------------- -%% Internal exports. -%% ----------------------------------------------------------------------------- - --export([init/6]). --export([init_std_publld/2,clean_std_publld/1]). -%% ----------------------------------------------------------------------------- - -%% ----------------------------------------------------------------------------- -%% Constants. -%% ----------------------------------------------------------------------------- - --define(NAMED_MS_TAB,inviso_rt_meta_named_ms). - -%% ----------------------------------------------------------------------------- - - -%% ============================================================================= -%% Exported API (Meant to be used by a runtime component). -%% ============================================================================= - -%% start(TiData,Tracer)={ok,Pid} | {error,Reason} -%% start(TiData,Tracer,InitPublLDmfa,RemovePublLDmfa,CleanPublLDmf)= -%%     {ok,Pid} | {error,Reason} -%%   TiData={file,FileName}|{relay,Node} -%%   Tracer=pid()|port() -%%   FileName=string() -%%   InitPublLDmfa={Mod,Func,ArgList} -%%   RemovePublLDmf={Mod,Func} | void -%%     RemovePublLDmf(PublLD)->nothing significant. -%%     These functions are called to create and destroy the public loopdata -%%     structure available to the meta-trace CallFunc and ReturnFunc. -%%   CleanPublLDmf={Mod,Func} -%%     This function will periodically be called to clean the public LD from -%%     pending meta-trace messages waiting for a corresponding return_from -%%     message. -%% -%% Starts a meta-tracer process, opening the ti-file specified in TiData. PublLD -%% is used to communicate data, typically between a call and return_from. -%% If no special initialization function is specified a standard one is used. -%% Note that the meta tracer function must know "who" is the regular tracer -%% (process or port). This because it must be possible to append {tracer,Tracer} -%% in meta match specs. -start(TiData,Tracer) -> -    Pid=spawn_link(?MODULE, -		   init, -		   [self(), -		    TiData, -		    Tracer, -		    {?MODULE,init_std_publld,[2,[]]}, -		    void, -		    {?MODULE,clean_std_publld}]), -    wait_for_reply(Pid). -start(TiData,Tracer,InitPublLDmfa,RemovePublLDmf,CleanPublLDmf) -> -    Pid=spawn_link(?MODULE, -		   init, -		   [self(),TiData,Tracer,InitPublLDmfa,RemovePublLDmf,CleanPublLDmf]), -    wait_for_reply(Pid). - -wait_for_reply(Pid) -> -    receive -	{Pid,ok} -> -	    {ok,Pid}; -	{Pid,{error,Reason}} -> -	    {error,Reason} -    after -	10000 ->                             % After very long time. -	    exit(Pid,kill),                  % It must be hanging. -	    {error,time_out} -    end. -%% ----------------------------------------------------------------------------- - -%% stop(Pid)=ok -%%   Pid=Adders to the meta tracer, pid(). -%% Shutsdown the metatracer. -stop(Pid) -> -    Pid ! {stop,self()}, -    ok. -%% ----------------------------------------------------------------------------- - -%% suspend(Pid)=ok -%%   Pid=Adders to the meta tracer, pid(). -%% Suspends the meta tracer by removing all meta trace patterns. -suspend(Pid) -> -    Pid ! {suspend,self()}, -    ok. -%% ----------------------------------------------------------------------------- - -%% init_tpm(Pid,Mod,Func,Arity,CallFunc)= -%% init_tpm(Pid,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=ok|{error,Reason}. -%%   Pid=Address to meta tracer process, pid(). -%%   Mod,Func=Pointing out the function which shall be meta traced, atom(). -%%   Arity=As above, integer(). -%%   InitFunc,RemoveFunc={Module,Function}|fun(), functions being called when -%%     to initialize the public loopdata structure, and to reset it. -%%       InitFunc(Mod,Func,Arity,PublLD)->{ok,NewPublLD,Output} -%%         Supposed to initialize whatever needs to be done before -%%         handling any incoming meta-trace message for the Mod:Func/Arity. -%%       RemoveFunc(Mod,Func,Arity,PublLD)->{ok,NewPublLD} -%%         Called when meta tracing of Mod:Func/Arity is stopped. It is supposed -%%         to clear datastructures away from the PublLD. -%% Initializes the public loopdata for this function. Note that we can not use wildcards -%% here (even if it is perfectly legal in Erlang). It also sets the CallFunc and -%% ReturnFunc for the meta traced function. The function is hence ready to be -%% meta traced with either tpm/5 or tpm_ms/5. -%% This function is synchronous, waiting for a reply from the meta server. -init_tpm(Pid,Mod,Func,Arity,CallFunc) -> -    init_tpm(Pid,Mod,Func,Arity,void,CallFunc,void,void). -init_tpm(Pid,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> -    send_wait(Pid, -	      {init_tpm,{Mod,Func,Arity},InitFunc,CallFunc,ReturnFunc,RemoveFunc}). -%% ----------------------------------------------------------------------------- - -%% tpm(Pid,Mod,Func,Arity,MatchSpec)={ok,N}|{error,Reason} -%% tpm(Pid,Mod,Func,Arity,MatchSpec,CallFunc)={ok,N}|{error,Reason} -%% tpm(Pid,Mod,Func,Arity,MatchSpec,InitFunc,CallFunc,ReturnFunc,RemoveFunc)= -%%   Pid=Address to meta tracer process, pid(). -%%   Mod,Func=Pointing out the function which shall be meta traced, atom(). -%%   Arity=As above, integer(). -%%   MatchSpec=List of match specification, possibly empty. Remember {return_trace} -%%     if expecting return_from messages. -%%   InitFunc,CallFunc,ReturnFunc,RemoveFunc={Module,Function}|fun(), -%%     functions being called when these functions are called by the meta trace -%%     server at certain events. -%%       CallFunc(CallingPid,ActualArgList,PublLD)->{ok,NewPrivLD,Output} -%%       ReturnFunc(CallingPid,ReturnValue,PublLD)->{ok,NewPrivLD,Output} -%%         When a call respectively return_from trace message arrives for the meta -%%         traced function, the corresponding function is called. -%%         The ReturnFunc must handle the fact that a return_from message arrives -%%         for a call which was never noticed. This because the message queue of the -%%         meta tracer may have been emptied. -%%   Reason=badarg |  -%%   Output=Characters to be written to the ti-file, bin() | 'void' -%% The tpm/5 function simply starts meta tracing for the function. It must -%% previously have been initialized. -%% tpm/6 & /9 initializes the function and starts meta tracing. -tpm(Pid,Mod,Func,Arity,MatchSpec) -  when is_atom(Mod),is_atom(Func),is_integer(Arity),is_list(MatchSpec),Mod/='_',Func/='_'-> -    send_wait(Pid,{tpm,{Mod,Func,Arity,MatchSpec}}); -tpm(_,_,_,_,_) -> -    {error,badarg}. - -tpm(Pid,Mod,Func,Arity,MatchSpec,CallFunc) -> -    tpm(Pid,Mod,Func,Arity,MatchSpec,void,CallFunc,void,void). - -tpm(Pid,Mod,Func,Arity,MatchSpec,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -  when is_atom(Mod),is_atom(Func),is_integer(Arity),is_list(MatchSpec),Mod/='_',Func/='_' -> -    send_wait(Pid,{tpm,{Mod,Func,Arity,MatchSpec},InitFunc,CallFunc,ReturnFunc,RemoveFunc}); -tpm(_,_,_,_,_,_,_,_,_) -> -    {error,badarg}. -%% ----------------------------------------------------------------------------- - -%% Same as tpm/X but the meta tracer will automatically append {tracer,Tracer} -%% to the enable list in a {trace,Disable,Enable} match spec action term. -tpm_tracer(Pid,Mod,Func,Arity,MatchSpec) -  when is_atom(Mod),is_atom(Func),is_integer(Arity),is_list(MatchSpec),Mod/='_',Func/='_'-> -    send_wait(Pid,{tpm_tracer,{Mod,Func,Arity,MatchSpec}}); -tpm_tracer(_,_,_,_,_) -> -    {error,badarg}. - -tpm_tracer(Pid,Mod,Func,Arity,MatchSpec,CallFunc) -> -    tpm_tracer(Pid,Mod,Func,Arity,MatchSpec,void,CallFunc,void,void). - -tpm_tracer(Pid,Mod,Func,Arity,MatchSpec,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -  when is_atom(Mod),is_atom(Func),is_integer(Arity),is_list(MatchSpec),Mod/='_',Func/='_' -> -    send_wait(Pid,{tpm_tracer, -		   {Mod,Func,Arity,MatchSpec}, -		   InitFunc,CallFunc,ReturnFunc,RemoveFunc}); -tpm_tracer(_,_,_,_,_,_,_,_,_) -> -    {error,badarg}. -%% ----------------------------------------------------------------------------- - -%% tpm_ms(Pid,Mod,Func,Arity,MSname,MS)={ok,N}|{error,Reason} -%%   Pid=Address to meta tracer process, pid(). -%%   Mod,Func=Pointing out the function to which we shall add a match-spec., atom(). -%%   Arity=As above, integer(). -%%   MSname=A name to be used if this MS shall be removed later. term(). -%%   MatchSpec=List of match specification, Remember {return_trace} -%%     if expecting return_from messages. -%% This function adds a list of match-specs to the already existing ones. It -%% uses an internal database to keep track of existing match-specs. If the -%% match-spec does not result in any meta traced functions (for whatever reason), -%% the MS is not saved in the database. The previously known match-specs are -%% not removed. -tpm_ms(Pid,Mod,Func,Arity,MSname,MS) -> -    send_wait(Pid,{tpm_ms,{Mod,Func,Arity},MSname,MS}). -%% ----------------------------------------------------------------------------- - -%% Same as tpm_ms/6 but the meta tracer will automatically append {tracer,Tracer} -%% to the enable list in a {trace,Disable,Enable} match spec action term. -tpm_ms_tracer(Pid,Mod,Func,Arity,MSname,MS) -> -    send_wait(Pid,{tpm_ms_tracer,{Mod,Func,Arity},MSname,MS}). -%% ----------------------------------------------------------------------------- - -%% ctpm_ms(Pid,Mod,Func,Arity)=ok -%% -%% Removes a names match-spec from the meta traced function. Note that is never -%% a fault to remove an MS. Not even from a function which is non existant. -ctpm_ms(Pid,Mod,Func,Arity,MSname) -> -    send_wait(Pid,{ctpm_ms,{Mod,Func,Arity},MSname}). -%% ----------------------------------------------------------------------------- - -%% Quick versions for erlang:register/2 which also uses a default CallFunc -%% and a default ReturnFunc. -local_register(Pid) -> -    Res1=tpm(Pid, -	     erlang,register,2,[{'_',[],[{exception_trace}]}], -	     fun metafunc_init/4,fun local_register_call/3, -	     fun local_register_return/3,void), -    Res2=tpm(Pid, -	     erlang,unregister,1,[], -	     void,fun local_unregister_call/3,void,void), -    {Res1,Res2}. -%% ----------------------------------------------------------------------------- - -%% Quick version for global:register_name/2, /3. -global_register(Pid) -> -    Res1=tpm(Pid,global,handle_call,3,[{[{register,'_','_','_'},'_','_'],[],[]}], -	void,fun global_register_call/3,void,void), -    Res2=tpm(Pid,global,delete_global_name,2,[], -	     void,fun global_unregister_call/3,void,void), -    {Res1,Res2}. -%% ----------------------------------------------------------------------------- - -%% ctpm(Pid,Mod,Func,Arity)=ok|{error,bad_mfa} -%% -%% Removes the meta trace pattern for the function, means stops generating output -%% for this function. The public LD may be cleared by the previously entered -%% RemoveFunc. -ctpm(Pid,Mod,Func,Arity) -> -    send_wait(Pid,{ctpm,{Mod,Func,Arity}}). -%% ----------------------------------------------------------------------------- - -%% remove_local_register(Pid)={Res1,Res2} -%%   Res1,Res2=ok|{error,Reason} -remove_local_register(Pid) -> -    Res1=ctpm(Pid,erlang,register,2), -    Res2=ctpm(Pid,erlang,unregister,1), -    {Res1,Res2}. -%% ----------------------------------------------------------------------------- - -%% remove_global_register(Pid)={Res1,Res2} -%%   Res1,Res2=ok|{error,Reason} -remove_global_register(Pid) -> -    Res1=ctpm(Pid,global,handle_call,3), -    Res2=ctpm(Pid,global,delete_global_name,2), -    {Res1,Res2}. -%% ----------------------------------------------------------------------------- - -%% Exported help functions which may be used in programming CallFunc and/or -%% ReturnFunc. Useful if the call is done on one node but must trigger the -%% start of something at other nodes. -metacast_call(Nodes,OrigPid,M,F,Args) -> -    multicast(Nodes,{trace_ts,OrigPid,call,{M,F,Args},void}), -    ok. - -metacast_return_from(Nodes,OrigPid,M,F,Arity,Value) -> -    multicast(Nodes,{trace_ts,OrigPid,return_from,{M,F,Arity},Value,void}), -    ok. - -multicast([Node|Rest],Msg) -> -    {?MODULE,Node} ! Msg, -    multicast(Rest,Msg); -multicast([],_) -> -    true. -%% ----------------------------------------------------------------------------- - -%% get_states(Pid)={ok,LD,PubLD}. -get_state(Pid) -> -    send_wait(Pid,get_state). -%% ----------------------------------------------------------------------------- - - -send_wait(To,Msg) -> -    Ref=make_ref(), -    MRef=erlang:monitor(process,To), -    To ! {Msg,Ref,self()}, -    receive -	{inviso_rt_meta_reply,Ref,Reply} -> -	    erlang:demonitor(MRef), -	    Reply; -	{'DOWN',MRef,_,_To,_Reason} -> -	    {error,no_metatracer} -    end. - -reply(To,Ref,Reply) -> -    To ! {inviso_rt_meta_reply,Ref,Reply}. -%% ----------------------------------------------------------------------------- - -%% ============================================================================= -%% Special API. -%% ============================================================================= - -%% write_ti(OutPut)= -%%   OutPut=binary() -%% Makes an extra entry into the trace information file (ti-file). This is useful -%% if a pid-alias association is learned in another way than through a meta traced -%% function call. Note that this API can only be used locally at the node in -%% question. -write_ti(OutPut) -> -    catch ?MODULE ! {write_ti,OutPut}. -%% ----------------------------------------------------------------------------- - - -%% ============================================================================= -%% API intended to be used on CallFuncs and RemoveFuncs. -%% ============================================================================= - -%% The reason there must be a special API for CallFuncs and RemoveFuncs are is -%% that those functions are executed inside *this* process context. Hence they -%% can not make function calls requiering this process to receive messages. - -%% Returns the tracer used for regular tracing. The reason this is implemented -%% in this way is that this function is intended to be used in meta trace call- -%% back functions. And there we can not have message passing API:s to the meta -%% trace(!). -get_tracer() -> -    get(tracer). -%% ----------------------------------------------------------------------------- - -%% Function equivalent to inviso_rt:tpm_ms/6. This function can *only* be used -%% inside a CallFunc or a RemoveFunc. -tpm_ms(Mod,Func,Arity,MSname,MS) -> -    case check_mfarity_exists(Mod,Func,Arity) of -	yes ->                               % Ok, and args must be ok then also. -	    {ok,h_tpm_ms(Mod,Func,Arity,MSname,MS)}; -	no -> -	    {error,not_initiated} -    end. -%% ----------------------------------------------------------------------------- - -tpm_ms_tracer(Mod,Func,Arity,MSname,MS) -> -    case check_mfarity_exists(Mod,Func,Arity) of -	yes ->                               % Ok, and args must be ok then also. -	    NewMS=add_tracer(MS,get_tracer()), -	    {ok,h_tpm_ms(Mod,Func,Arity,MSname,NewMS)}; -	no -> -	    {error,not_initiated} -    end. -%% ----------------------------------------------------------------------------- - -%% Function that returns all MSname in use for Mod:Func/Arity -list_tpm_ms(Mod,Func,Arity) -> -    {ok,h_list_tpm_ms(Mod,Func,Arity)}. -%% ----------------------------------------------------------------------------- - -%% Function equivalent to inviso_rt:ctpm_ms/5. This function can *only* be used -%% inside a CallFunc or a RemoveFunc. -ctpm_ms(Mod,Func,Arity,MSname) -> -    h_ctpm_ms(Mod,Func,Arity,MSname), -    ok. -%% ----------------------------------------------------------------------------- - - -%% ============================================================================= -%% The server implemenation. -%% ============================================================================= - -init(Parent,TiData,Tracer,InitPublLDmfa,RemovePublLDmf,CleanPublLDmf) -> -    process_flag(priority,high),            % Since we may receive from many procs. -    register(?MODULE,self()),               % So we can act as relay receiver. -    case open_traceinfo_file(TiData) of -	{ok,TI} ->                          % The ti.-file. -	    TId=ets:new(?NAMED_MS_TAB,[named_table,set,protected]), -	    PublLD=do_init_publ_ld(InitPublLDmfa), -	    Parent ! {self(),ok}, -	    put(tracer,Tracer),             % Uggly quick fix! -	    loop(Parent, -		 Tracer, -		 TI, -		 mk_new_ld(InitPublLDmfa,RemovePublLDmf,CleanPublLDmf,TId), -		 PublLD, -		 now()); -	{error,Reason} -> -	    Parent ! {self(),{error,Reason}} -    end. -%% ----------------------------------------------------------------------------- - -loop(Parent,Tracer,TI,LD,PrevPublLD,PrevCleanTime) -> -    {PublLD,CleanTime}=throw_old_failed(get_cleanpublldmf_ld(LD),PrevPublLD,PrevCleanTime), -    receive -	{{init_tpm,{Mod,Func,Arity},InitFunc,CallFunc,ReturnFunc,RemoveFunc},Ref,Parent} -> -	    case check_mfarity_exists(Mod,Func,Arity) of -		no ->                       % Good then we can add it! -		    case check_tpm_args(Mod,Func,Arity) of -			true ->             % Args are ok. -			    {NewLD,NewPublLD}= -				h_init_tpm(Mod,Func,Arity, -					   InitFunc,CallFunc,ReturnFunc,RemoveFunc, -					   TI,LD,PublLD), -			    reply(Parent,Ref,ok), -			    loop(Parent,Tracer,TI,NewLD,NewPublLD,CleanTime); -			false ->            % Faulty arguments, -			    reply(Parent,Ref,{error,bad_mfa}), -			    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -		    end; -		yes ->                      % If it already exists, cant init again. -		    reply(Parent,Ref,{error,already_initiated}), -		    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -	    end; -	{{tpm,{Mod,Func,Arity,MS},InitFunc,CallFunc,ReturnFunc,RemoveFunc},Ref,Parent} -> -	    case check_mfarity_exists(Mod,Func,Arity) of -		no ->                       % Good then we can add it! -		    case check_tpm_args(Mod,Func,Arity) of -			true ->             % Args are ok. -			    {NewLD,NewPublLD,N}= -				h_tpm(Mod,Func,Arity,MS, -				      InitFunc,CallFunc,ReturnFunc,RemoveFunc, -				      TI,LD,PublLD), -			    reply(Parent,Ref,{ok,N}), -			    loop(Parent,Tracer,TI,NewLD,NewPublLD,CleanTime); -			false -> -			    reply(Parent,Ref,{error,bad_mfa}), -			    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -		    end; -		yes -> -		    reply(Parent,Ref,{error,already_initiated}), -		    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -	    end; -	{{tpm,{Mod,Func,Arity,MS}},Ref,Parent} -> -	    case check_mfarity_exists(Mod,Func,Arity) of -		yes ->                      % Ok, and args must be ok then also. -		    {NewLD,N}=h_tpm(Mod,Func,Arity,MS,LD), -		    reply(Parent,Ref,{ok,N}), -		    loop(Parent,Tracer,TI,NewLD,PublLD,CleanTime); -		no ->                       % Must be initiated before. -		    reply(Parent,Ref,{error,not_initiated}), -		    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -	    end; -	{{tpm_tracer,{Mod,Func,Arity,MS},InitFunc,CallFunc,ReturnFunc,RemoveFunc},Ref,Parent} -> -	    case check_mfarity_exists(Mod,Func,Arity) of -		no ->                       % Good then we can add it! -		    case check_tpm_args(Mod,Func,Arity) of -			true ->             % Args are ok. -			    NewMS=add_tracer(MS,Tracer), -			    {NewLD,NewPublLD,N}= -				h_tpm(Mod,Func,Arity,NewMS, -				      InitFunc,CallFunc,ReturnFunc,RemoveFunc, -				      TI,LD,PublLD), -			    reply(Parent,Ref,{ok,N}), -			    loop(Parent,Tracer,TI,NewLD,NewPublLD,CleanTime); -			false -> -			    reply(Parent,Ref,{error,bad_mfa}), -			    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -		    end; -		yes -> -		    reply(Parent,Ref,{error,already_initiated}), -		    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -	    end; -	{{tpm_tracer,{Mod,Func,Arity,MS}},Ref,Parent} -> -	    case check_mfarity_exists(Mod,Func,Arity) of -		yes ->                      % Ok, and args must be ok then also. -		    NewMS=add_tracer(MS,Tracer), -		    {NewLD,N}=h_tpm(Mod,Func,Arity,NewMS,LD), -		    reply(Parent,Ref,{ok,N}), -		    loop(Parent,Tracer,TI,NewLD,PublLD,CleanTime); -		no ->                       % Must be initiated before. -		    reply(Parent,Ref,{error,not_initiated}), -		    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -	    end; -	{{tpm_ms,{Mod,Func,Arity},MSname,MS},Ref,Parent} -> -	    case check_mfarity_exists(Mod,Func,Arity) of -		yes ->                      % Ok, and args must be ok then also. -		    reply(Parent,Ref,{ok,h_tpm_ms(Mod,Func,Arity,MSname,MS)}), -		    loop(Parent,Tracer,TI,LD,PublLD,CleanTime); -		no -> -		    reply(Parent,Ref,{error,not_initiated}), -		    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -	    end; -	{{tpm_ms_tracer,{Mod,Func,Arity},MSname,MS},Ref,Parent} -> -	    case check_mfarity_exists(Mod,Func,Arity) of -		yes ->                      % Ok, and args must be ok then also. -		    NewMS=add_tracer(MS,Tracer), -		    reply(Parent,Ref,{ok,h_tpm_ms(Mod,Func,Arity,MSname,NewMS)}), -		    loop(Parent,Tracer,TI,LD,PublLD,CleanTime); -		no -> -		    reply(Parent,Ref,{error,not_initiated}), -		    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -	    end; -	{{ctpm_ms,{Mod,Func,Arity},MSname},Ref,Parent} -> -	    reply(Parent,Ref,ok), -	    h_ctpm_ms(Mod,Func,Arity,MSname), -	    loop(Parent,Tracer,TI,LD,PublLD,CleanTime); -	{{ctpm,{Mod,Func,Arity}},Ref,Parent} -> -	    case get_remove_func_ld(Mod,Func,Arity,LD) of -		false ->                    % Incorrect Mod:Func/Arity! -		    reply(Parent,Ref,{error,bad_mfa}), -		    loop(Parent,Tracer,TI,LD,PublLD,CleanTime); % Do nothing! -		MF ->                       % {M,F}, Func or 'void'. -		    catch erlang:trace_pattern({Mod,Func,Arity},false,[meta]), -		    NewPublLD=do_removefunc(MF,Mod,Func,Arity,PublLD), -		    NewLD=ctpm_ld(Mod,Func,Arity,LD), -		    reply(Parent,Ref,ok), -		    loop(Parent,Tracer,TI,NewLD,NewPublLD,CleanTime) -	    end; -	{suspend,Parent} ->                 % Removes all meta trace patterns. -	    stop_all_meta_tracing(get_all_meta_funcs_ld(LD),PublLD,LD), -	    do_remove_publ_ld(get_removepublldmf_ld(LD),PublLD), -	    NewPublLD=do_init_publ_ld(get_initpublldmfa_ld(LD)), -	    loop(Parent,Tracer,TI,reset_ld(LD),NewPublLD,CleanTime); -	{stop,Parent} ->                    % Make a controlled shutdown. -	    stop_all_meta_tracing(get_all_meta_funcs_ld(LD),PublLD,LD), -	    do_remove_publ_ld(get_removepublldmf_ld(LD),PublLD), -	    close_traceinfo_file(TI);       % And then simply terminate. -	{trace_ts,Pid,call,{M,F,Args},TS} -> -	    case handle_meta(get_call_func_ld(M,F,length(Args),LD),Pid,{call,Args,TS},PublLD) of -		{ok,NewPublLD,Output} when is_binary(Output);is_list(Output) -> -		    write_output(TI,Output), -		    loop(Parent,Tracer,TI,LD,NewPublLD,CleanTime); -		{ok,NewPublLD,_} ->         % No output to the ti-file this time. -		    loop(Parent,Tracer,TI,LD,NewPublLD,CleanTime); -		_ ->                        % Not handled correct, not much to do. -		    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -	    end; -	{trace_ts,Pid,TypeTag,{M,F,Arity},Value,TS} -	  when TypeTag==return_from;TypeTag==exception_from -> -	    case handle_meta(get_return_func_ld(M,F,Arity,LD),Pid,{TypeTag,Value,TS},PublLD) of -		{ok,NewPublLD,Output} when is_binary(Output);is_list(Output) -> -		    write_output(TI,Output), -		    loop(Parent,Tracer,TI,LD,NewPublLD,CleanTime); -		{ok,NewPublLD,_} ->         % No output to the ti-file this time. -		    loop(Parent,Tracer,TI,LD,NewPublLD,CleanTime); -		_ ->                        % Not handled correct, not much to do. -		    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -	    end; -	{relayed_meta,Bin} -> -	    write_output(TI,Bin), -	    loop(Parent,Tracer,TI,LD,PublLD,CleanTime); -	{write_ti,OutPut} -> -	    write_output(TI,OutPut), -	    loop(Parent,Tracer,TI,LD,PublLD,CleanTime); -	{get_state,Ref,From} ->             % Debug function. -	    reply(From,Ref,{ok,LD,PublLD}), -	    loop(Parent,Tracer,TI,LD,PublLD,CleanTime); -	_Other -> -	    loop(Parent,Tracer,TI,LD,PublLD,CleanTime) -    end. - - -%% ============================================================================= -%% First level help functions. -%% ============================================================================= - -%% Function which opens the trace-information file(s). It must understand -%% the tidata specification which is part of the tracerdata given to the -%% runtime component during init_tracing. -%% It must return an internal notation of the time of file open and a -%% useful descriptor the write_output function can use. -%% Returns {ok,TiDescriptor} or {error,Reason}. -open_traceinfo_file({file,FileName}) ->     % A plain raw binary file. -    case file:open(FileName,[write,raw,binary]) of -	{ok,FD} -> -	    {ok,{file,FD}}; -	{error,Reason} -> -	    {error,{open,[FileName,Reason]}} -    end; -open_traceinfo_file({relay,ToNode}) ->      % Use distributed Erlang. -    {ok,{relay,ToNode}}; -open_traceinfo_file(IncorrectTI) -> -    {error,{badarg,IncorrectTI}}. -%% ----------------------------------------------------------------------------- - -close_traceinfo_file({file,FD}) -> -    file:close(FD); -close_traceinfo_file(_) -> -    ok. -%% ----------------------------------------------------------------------------- - -%% Help function handling initializing meta tracing of a function. -%% Returns {NewLD,NewPublLD}. -h_init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc,TI,LD,PublLD) -> -    case do_initfunc(InitFunc,Mod,Func,Arity,PublLD) of -	{NewPublLD,Output} -> -	    write_output(TI,Output), -	    NewLD=init_tpm_ld(Mod,Func,Arity,CallFunc,ReturnFunc,RemoveFunc,LD), -	    {NewLD,NewPublLD}; -	false ->                            % The initfunc did not do anything. -	    NewLD=init_tpm_ld(Mod,Func,Arity,CallFunc,ReturnFunc,RemoveFunc,LD), -	    {NewLD,PublLD} -    end. -%% ----------------------------------------------------------------------------- - -%% Help function handling initializing meta tracing of a function and also -%% set the meta trace pattern as specified. -%% Returns {NewLD,NewPublLD,N}. -h_tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc,TI,LD,PublLD) -> -    {NewLD,NewPublLD}= -	h_init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc,TI,LD,PublLD), -    case set_meta_tracing(Mod,Func,Arity,MS) of -	true ->                              % Ok, set one pattern. -	    {NewLD,NewPublLD,1}; -	false -> -	    {NewLD,NewPublLD,0} -    end. -%% ----------------------------------------------------------------------------- - -%% Help function handling setting meta trace patter for a function which has -%% already been intialized. Note that we must remove all potentially stored -%% match-specs, if this function has been given match-specs before with -%% tpm_ms. -%% Returns a {NewLD,N}. -h_tpm(Mod,Func,Arity,MS,LD) -> -    case set_meta_tracing(Mod,Func,Arity,MS) of -	true -> -	    {remove_ms_ld(Mod,Func,Arity,LD),1}; -	false -> -	    {LD,0} -    end. -%% ----------------------------------------------------------------------------- - -%% Help function that adds a match-spec to Mod:Func/Arity. It is not defined -%% in which order the match-specs will be given to the BIF. -%% Note that if an MS with the same name as an exiting is inserted, the previous -%% match-spec will be removed. -%% Very important to realise is that the empty meta match spec [] imposes no -%% restrictions what so ever on the generating of meta trace call messages. -%% Uncontrolled sending of such messages may quickly drain power from the system. -%% Since an empty match-spec will "disappear" when added to other match specs, -%% the empty match is transformed to what it actually is: [{'_',[],[]}]. -%% Returns 0 or 1 indicating failure or success. -h_tpm_ms(Mod,Func,Arity,MSname,MS) -> -    MSsNames=get_ms_ld(Mod,Func,Arity),     % Fetch all previous match-specs. -    TransformedMS=h_tpm_ms_convert_null_ms(MS), -    MSsNames1=lists:keydelete(MSname,1,MSsNames), % If it already existed, it is gone! -    NewMSs=lists:flatten([TransformedMS,lists:map(fun({_Name,MSx})->MSx end,MSsNames1)]), -    case set_meta_tracing(Mod,Func,Arity,NewMSs) of -	true ->                             % We only save the MS if it was good. -	    put_ms_ld(Mod,Func,Arity,MSname,TransformedMS,MSsNames1), -	    1; -	false -> -	    0 -    end. - -%% Help function converting the null match spec into, still a null match spec, -%% on a proper match spec format. This because it will otherwise be difficult -%% to see the difference between no active tpm_ms and all a set of null ms. -h_tpm_ms_convert_null_ms([]) -> -    [{'_',[],[]}]; -h_tpm_ms_convert_null_ms(MS) -> -    MS. -%% ----------------------------------------------------------------------------- - -%% Help function returning a list of all names used for match-functions for -%% the Mod:Func/Arity in question. -h_list_tpm_ms(Mod,Func,Arity) -> -    MSsNames=get_ms_ld(Mod,Func,Arity),     % A list of {MSname,MS}. -    lists:map(fun({MSname,_})->MSname end,MSsNames). -%% ----------------------------------------------------------------------------- - -%% Function that removes a named match-spec. Returns nothing significant. -%% Note that if we end up with no match-specs, we must remove the meta trace -%% patten all together. That is bringing the function back to just initiated. -h_ctpm_ms(Mod,Func,Arity,MSname) -> -    case get_ms_ld(Mod,Func,Arity) of -	[] ->                               % The name does certainly not exist! -	    true;                           % We don't have to do anything. -	MSsNames -> -	    case lists:keysearch(MSname,1,MSsNames) of -		{value,{_,_MS}} ->          % Ok, we must do something! -		    NewMSsNames=lists:keydelete(MSname,1,MSsNames), -		    case lists:flatten(lists:map(fun({_Name,MS})->MS end,NewMSsNames)) of -			[] ->               % This means stop meta tracing. -			    set_meta_tracing(Mod,Func,Arity,false); -			NewMSs -> -			    set_meta_tracing(Mod,Func,Arity,NewMSs) -		    end, -		    set_ms_ld(Mod,Func,Arity,NewMSsNames); -		false ->                    % But this name does not exist. -		    true                    % So we do not have to do anything. -	    end -    end. -%% ----------------------------------------------------------------------------- - -%% Function that checks the arguments to the meta trace pattern. The reason we -%% must do this is that we can only allow meta tracing on specific functions and -%% not using wildpatterns. Otherwise the meta trace server will not understand -%% which callfunc for instance to call when a meta-trace message is generated -%% for a function. -%% Returns 'true' or 'false'. -check_tpm_args(Mod,Func,Arity) -  when is_atom(Mod),is_atom(Func),is_integer(Arity),Mod/='_',Func/='_' -> -    true; -check_tpm_args(_,_,_) -> -    false. -%% ----------------------------------------------------------------------------- - -%% Help function which calls the actual BIF setting meta-trace-patterns. -%% Returns 'true' or 'false'. -set_meta_tracing(Mod,Func,Arity,MS) when is_atom(Mod) -> -    case erlang:module_loaded(Mod) of -	true -> -	    set_meta_tracing_2(Mod,Func,Arity,MS); -	false ->                            % The module is not loaded. -	    case code:ensure_loaded(Mod) of -		{module,_Mod} -> -		    set_meta_tracing_2(Mod,Func,Arity,MS); -		{error,_Reason} ->          % Could not load the module. -		    false                   % No use try to trace. -	    end -    end; -set_meta_tracing(_,_,_,_) -> -    false. - -set_meta_tracing_2(Mod,Func,Arity,MS) -> -    case catch erlang:trace_pattern({Mod,Func,Arity},MS,[meta]) of -	0 ->                                % Hmm, nothing happend :-) -	    false; -	N when is_integer(N) ->                % The normal case, some functions were hit. -	    true; -	{'EXIT',_Reason} -> -	    false -    end. -%% ----------------------------------------------------------------------------- - -%% Help function which removes all meta trace pattern for the functions mentioned -%% in the list being first argument. It also executes the remove funcs for each -%% and every no longer meta traced function. This done since some of the remove -%% functions may do side-effects (like deleteing ETS tables). -%% Returns nothing significant. -stop_all_meta_tracing([{M,F,Arity}|Rest],PublLD,LD) -> -    catch erlang:trace_pattern({M,F,Arity},false,[meta]), -    NewPublLD=do_removefunc(get_remove_func_ld(M,F,Arity,LD),M,F,Arity,PublLD), -    stop_all_meta_tracing(Rest,NewPublLD,LD); -stop_all_meta_tracing([],_,_) -> -    true. -%% ----------------------------------------------------------------------------- - -%% This function calls the function registered to be handler for a certain -%% meta-traced function. Such a function or fun must take three arguments -%% and return {ok,NewPrivLD,OutPutBinary} or 'false'. OutPutBinary may be -%% something else, and is then ignored. -handle_meta({M,F},Pid,Arg1,PrivLD) -> -    (catch M:F(Pid,Arg1,PrivLD)); -handle_meta(Fun,Pid,Arg1,PrivLD) when is_function(Fun) -> -    (catch Fun(Pid,Arg1,PrivLD)); -handle_meta(_,_,_,_) ->                     % Don't know how to do this. -    false. -%% ----------------------------------------------------------------------------- - -%% Help function writing output from a callback function to the ti-file. -%% Output can be a binary or a list of binaries. -write_output(TI,[OutPut|Rest]) -> -    write_output(TI,OutPut), -    write_output(TI,Rest); -write_output({file,FD},Bin) when is_binary(Bin) -> % Plain direct-binary file -    Size=byte_size(Bin), -    file:write(FD,list_to_binary([<<0,Size:32>>,Bin])); -write_output({relay,ToNode},Bin) when is_atom(ToNode),is_binary(Bin) -> -    {inviso_rt_meta,ToNode} ! {relayed_meta,Bin}; -write_output(_,_) ->                        % Don't understand, just skip. -    true. -%% ----------------------------------------------------------------------------- - - -%% ============================================================================= -%% Various help functions. -%% ============================================================================= - -%% Help function initializing the public loopdata structure. Note that if the -%% supplied InitPublLDmfa is faulty we let the structure become the error. -%% The error will most likely turn up in an error report somewhere, eventually. -do_init_publ_ld({M,F,Args}) when is_atom(M),is_atom(F),is_list(Args) -> -    case catch apply(M,F,Args) of -	{'EXIT',_Reason} -> -	    {error,init_publ_ld_func};      % Let the struct be this error! -	InitialPublLD -> -	    InitialPublLD -    end; -do_init_publ_ld(_) -> -    {error,init_publ_ld_func}. -%% ----------------------------------------------------------------------------- - -%% Help function which removes the public loopdata structure. The function does -%% not necessarily have to exist. Returns nothing significant. -do_remove_publ_ld({M,F},PublLD) when is_atom(M),is_atom(F) -> -    catch M:F(PublLD); -do_remove_publ_ld(_,_) -> -    true. -%% -----------------------------------------------------------------------------	 - -%% Hlp function initializing a particular meta traced function into the public -%% loopdata. Note that the function is not mandatory. -%% Returns {NewPublLD,Output} or 'false'. -do_initfunc({M,F},Mod,Func,Arity,PublLD) when is_atom(M),is_atom(F) -> -    case catch M:F(Mod,Func,Arity,PublLD) of -	{ok,NewPublLD,Output} -> -	    {NewPublLD,Output}; -	_ ->                                % Everything else is an error. -	    false                           % Act as no initialization function. -    end; -do_initfunc(Fun,Mod,Func,Arity,PublLD) when is_function(Fun) -> -    case catch Fun(Mod,Func,Arity,PublLD) of -	{ok,NewPublLD,Output} -> -	    {NewPublLD,Output}; -	_ ->                                % Everything else is an error. -	    false                           % Act as no initialization function. -    end; -do_initfunc(_,_,_,_,_) ->                   % Perhaps too generous, should be 'void' only. -    false. -%% ----------------------------------------------------------------------------- - -%% Help function removing a particular meta traced function from the public -%% loopdata. Note that we do not make much noice should the call back function -%% be faulty. -do_removefunc({M,F},Mod,Func,Arity,PublLD) when is_atom(M),is_atom(F) -> -    case catch M:F(Mod,Func,Arity,PublLD) of -	{ok,NewPublLD} -> -	    NewPublLD; -	_ ->                                % Everything else is an error. -	    PublLD                          % Act as no initialization function. -    end; -do_removefunc(Fun,Mod,Func,Arity,PublLD) when is_function(Fun) -> -    case catch Fun(Mod,Func,Arity,PublLD) of -	{ok,NewPublLD} -> -	    NewPublLD; -	_ ->                                % Everything else is an error. -	    PublLD                          % Act as no initialization function. -    end; -do_removefunc(_,_,_,_,PublLD) -> -    PublLD. -%% ----------------------------------------------------------------------------- - -%% Function that, if the time has come, goes through the priv-ld structure and -%% cleans away entryn left behind. The usual cause is that the function call -%% caused an exception and there were therefore no matching return_from. -%% Returns {NewPrivLD,now()}. -throw_old_failed({M,F},PrivLD,PrevClean) -> -    case difference_in_now(PrevClean,now(),60) of % We clean once every minute. -	true -> -	    case catch apply(M,F,[PrivLD]) of -		{'EXIT',_Reason} ->         % Something went wrong, ignore it. -		    {PrivLD,now()};         % Just keep the old priv-ld. -		NewPrivLD ->                % The function must return a priv-ld. -		    {NewPrivLD,now()} -	    end; -	false ->                            % Not time yet! -	    {PrivLD,PrevClean} -    end. -%% ----------------------------------------------------------------------------- - -%% Help function comparing two now timestamps. Returns true or false depending -%% on if S2 is more than DiffS seconds after S1. Only works for differences -%% less than 1 million seconds. -difference_in_now({MegaS1,S1,_},{MegaS2,S2,_},DiffS) -> -    if -	MegaS1+1<MegaS2 ->                  % More than 1 Mega sec. difference. -	    true; -	MegaS1==MegaS2,S1+DiffS<S2 -> -	    true; -	MegaS1+1==MegaS2,S1+DiffS<S2+1000000 -> -	    true; -	true -> -	    false -    end. -%% ----------------------------------------------------------------------------- - -%% This help function adds a {tracer,Tracer} to the enable-list in a 'trace' -%% match spec action. The reason for this is that the author of the a meta -%% match spec meant to turn tracing on for the process executing the match spec -%% can not know the tracer. This since the match spec is most likely authored -%% at the control component's node, and not here. -%% Note the double tuple necessary to make it just precise a tuple! -%% Returns a new match spec. -add_tracer([MS1|Rest],Tracer) -> -    [add_tracer_2(MS1,Tracer)|add_tracer(Rest,Tracer)]; -add_tracer([],_) -> -    []; -add_tracer(NotList,_Tracer) ->              % Can be 'false', but also an error. -    NotList. - -add_tracer_2({Head,Cond,Body},Tracer) -> -    {Head,Cond,add_tracer_3(Body,Tracer)}; -add_tracer_2(Faulty,_Tracer) -> -    Faulty. - -add_tracer_3([{trace,Disable,Enable}|Rest],Tracer) when is_list(Enable) -> -    [{trace,Disable,Enable++[{{tracer,Tracer}}]}|Rest]; -add_tracer_3([ActionTerm|Rest],Tracer) -> -    [ActionTerm|add_tracer_3(Rest,Tracer)]; -add_tracer_3([],_Tracer) -> -    []; -add_tracer_3(FaultyBody,_Tracer) -> -    FaultyBody. -%% ----------------------------------------------------------------------------- - -%% ----------------------------------------------------------------------------- -%% Help functions handling internal loopdata. -%% ----------------------------------------------------------------------------- - --record(ld,{init_publ_ld_mfa,               % {M,F,Args} -	    remove_publ_ld_mf,              % {M,F} | void -	    clean_publ_ld_mf,               % {Mod,Func} -	    ms_mfarities=notable,           % ETS holding names match functions. -	    call_mfarities=[],              % [{{M,F,Arity},2-TupleOrFun},...] -	    return_mfarities=[],            % [{{M,F,Arity},2-TupleOrFun},...] -	    remove_mfarities=[] -	   }). - -mk_new_ld(InitPublLDmfa,RemovePublLDmf,CleanPublLDmf,TId) -> -    #ld{ -	   init_publ_ld_mfa=InitPublLDmfa, -	   remove_publ_ld_mf=RemovePublLDmf, -	   clean_publ_ld_mf=CleanPublLDmf, -	   ms_mfarities=TId -       }. -%% ----------------------------------------------------------------------------- - -%% Function which restores the internal loop data to somekind of initial state. -%% This is useful when tracing has been suspended. -reset_ld(#ld{init_publ_ld_mfa=InitPublLDmfa, -	     remove_publ_ld_mf=RemovePublLDmf, -	     clean_publ_ld_mf=CleanPublLDmf, -	     ms_mfarities=TId}) -> -    ets:match_delete(TId,{'_','_'}),        % Empty the table. -    #ld{init_publ_ld_mfa=InitPublLDmfa, -	remove_publ_ld_mf=RemovePublLDmf, -	clean_publ_ld_mf=CleanPublLDmf, -        ms_mfarities=TId}. -%% ----------------------------------------------------------------------------- - -get_initpublldmfa_ld(#ld{init_publ_ld_mfa=InitPublLDmfa}) -> -    InitPublLDmfa. -%% ----------------------------------------------------------------------------- - -get_removepublldmf_ld(#ld{remove_publ_ld_mf=RemovePublLDmf}) -> -    RemovePublLDmf. -%% ----------------------------------------------------------------------------- - -get_cleanpublldmf_ld(#ld{clean_publ_ld_mf=CleanPublLDmf}) -> -    CleanPublLDmf. -%% ----------------------------------------------------------------------------- - -%% Help function adding data associated with a meta traced function to the -%% internal loopdata. Called when meta tracing is activated for M:F/Arity. -init_tpm_ld(M,F,Arity,CallFunc,ReturnFunc,RemoveFunc,LD) -> -    ets:insert(LD#ld.ms_mfarities,{{M,F,Arity},[]}), -    CallFuncs=LD#ld.call_mfarities, -    ReturnFuncs=LD#ld.return_mfarities, -    RemoveFuncs=LD#ld.remove_mfarities, -    LD#ld{call_mfarities=[{{M,F,Arity},CallFunc}|CallFuncs], -	  return_mfarities=[{{M,F,Arity},ReturnFunc}|ReturnFuncs], -	  remove_mfarities=[{{M,F,Arity},RemoveFunc}|RemoveFuncs]}. -%% ----------------------------------------------------------------------------- - -%% Help function which answers the question if we have already initiated the -%% function. It is done by looking in the ETS-table with named match-functions. -%% If there is an entry in the set-type table for M:F/Arity, the function is -%% initiated. -%% Returns 'yes' or 'no'. -check_mfarity_exists(M,F,Arity) -> -    case ets:lookup(?NAMED_MS_TAB,{M,F,Arity}) of -	[] -> -	    no; -	[_] -> -	    yes -    end. -%% ----------------------------------------------------------------------------- - -%% Help function adding an entry with [{MSname,MSlist}|MSsNames] for M:F/Arity. -%% Note that any already existing entry is removed. -%% Returns nothing significant. -put_ms_ld(M,F,Arity,MSname,MS,MSsNames) -> -    ets:insert(?NAMED_MS_TAB,{{M,F,Arity},[{MSname,MS}|MSsNames]}). -%% ----------------------------------------------------------------------------- - -%% Help function taking a list of {MSname,MSs} and storing them in the -%% internal loop data structure. The storage is actually implemented as an ETS -%% table. Any previous list of {MSname,MSs} associated with this {M,F,Arity} will -%% be lost. Returns nothing significant. -set_ms_ld(M,F,Arity,MSsNames) -> -    ets:insert(?NAMED_MS_TAB,{{M,F,Arity},MSsNames}). -%% ----------------------------------------------------------------------------- - -%% Help function fetching a list of {MSname,MatchSpecs} for a M:F/Arity. The -%% match-functions are stored in an ETS table searchable on {M,F,Arity}. -get_ms_ld(M,F,Arity) -> -    case ets:lookup(?NAMED_MS_TAB,{M,F,Arity}) of -	[{_MFArity,MSsNames}] -> -	    MSsNames; -	[] -> -	    [] -    end. -%% ----------------------------------------------------------------------------- - -%% Help function removing all saved match-specs for a certain M:F/Arity. -%% Returns a new loopdata structure. -remove_ms_ld(M,F,Arity,LD) -> -    ets:delete(LD#ld.ms_mfarities,{M,F,Arity}), -    LD. -%% ----------------------------------------------------------------------------- - -%% Help function which removes all information about a meta traced function from -%% the internal loopdata. Returns a new loopdata structure. -ctpm_ld(M,F,Arity,LD) -> -    ets:delete(LD#ld.ms_mfarities,{M,F,Arity}), -    NewCallFuncs=lists:keydelete({M,F,Arity},1,LD#ld.call_mfarities), -    NewReturnFuncs=lists:keydelete({M,F,Arity},1,LD#ld.return_mfarities), -    NewRemoveFuncs=lists:keydelete({M,F,Arity},1,LD#ld.remove_mfarities), -    LD#ld{call_mfarities=NewCallFuncs, -	  return_mfarities=NewReturnFuncs, -	  remove_mfarities=NewRemoveFuncs}. -%% ----------------------------------------------------------------------------- - -get_call_func_ld(M,F,Arity,#ld{call_mfarities=CallFuncs}) -> -    case lists:keysearch({M,F,Arity},1,CallFuncs) of -	{value,{_,MF}} -> -	    MF; -	false -> -	    false -    end. -%% ----------------------------------------------------------------------------- - -get_return_func_ld(M,F,Arity,#ld{return_mfarities=CallFuncs}) -> -    case lists:keysearch({M,F,Arity},1,CallFuncs) of -	{value,{_,MF}} -> -	    MF; -	false -> -	    false -    end. -%% ----------------------------------------------------------------------------- - -get_remove_func_ld(M,F,Arity,#ld{remove_mfarities=RemoveFuncs}) -> -    case lists:keysearch({M,F,Arity},1,RemoveFuncs) of -	{value,{_,MF}} -> -	    MF; -	false -> -	    false -    end. -%% ----------------------------------------------------------------------------- - -%% Function returning a list of all {Mod,Func,Arity} which are currently meta -%% traced. It does do by listifying the call_mfarities field in the internal -%% loopdata. -get_all_meta_funcs_ld(#ld{call_mfarities=CallFuncs}) -> -    lists:map(fun({MFArity,_})->MFArity end,CallFuncs). -%% ----------------------------------------------------------------------------- - - -%% ============================================================================= -%% Functions for the standard PublLD structure. -%% -%% It is tuple {Part1,GlobalData} where Part1 is of length at least 2. -%% Where each field is a list of tuples. The last item in each tuple shall be -%% a now tuple, making it possible to clean it away should it be too old to be -%% relevant (there was no return_from message due to a failure). -%% Other fields can be used for other functions. -%% The GlobalData is not cleaned but instead meant to store data must be passed -%% to each CallFunc when a meta trace message arrives. -%% ============================================================================= -		       -%% Function returning our standard priv-loopdata structure. -init_std_publld(Size,GlobalData) -> -    {list_to_tuple(lists:duplicate(Size,[])),GlobalData}. -%% ----------------------------------------------------------------------------- - -%% Function capable of cleaning out a standard publ-ld. The last element of each -%% tuple must be the now item. -%% Returns a new publ-ld structure. -clean_std_publld({Part1,GlobalData}) -> -    {clean_std_publld_2(Part1,now(),tuple_size(Part1),[]),GlobalData}. - -clean_std_publld_2(_,_,0,Accum) -> -    list_to_tuple(Accum); -clean_std_publld_2(PublLD,Now,Index,Accum) -> -    NewTupleList=clean_std_publld_3(element(Index,PublLD),Now), -    clean_std_publld_2(PublLD,Now,Index-1,[NewTupleList|Accum]). - -clean_std_publld_3([Tuple|Rest],Now) -> -    PrevNow=element(tuple_size(Tuple),Tuple), % Last item shall be the now item. -    case difference_in_now(PrevNow,Now,30) of -	true ->                             % Remove it then! -	    clean_std_publld_3(Rest,Now); -	false ->                            % Keep it! -	    [Tuple|clean_std_publld_3(Rest,Now)] -    end; -clean_std_publld_3([],_) -> -    []. -%% ----------------------------------------------------------------------------- - -%% ============================================================================= -%% Functions used as handling functions (as funs) for registered process names. -%% (Given that we use the standard priv-ld, otherwise you must do your own!). -%% ============================================================================= - -%% Call-back for initializing the meta traced functions there are quick functions -%% for. Returns a new public loop data structure. -metafunc_init(erlang,register,2,{Part1,GlobalData}) -> -    {setelement(1,Part1,[]),GlobalData}. -%% ----------------------------------------------------------------------------- - -%% Call-function for erlang:register/2. -%% This function adds the call to register/2 to a standard priv-ld structure. -%% Note that we *must* search for previous entries from the same process. If such -%% still in structure it means a failed register/2 call. It must first be removed -%% so it can not be mixed up with this one. Since meta-trace message will arrive -%% in order, there was no return_from message for that call if we are here now. -local_register_call(CallingPid,{call,[Alias,Pid],TS},{Part1,GlobalData}) -> -    TupleList=element(1,Part1),             % The register/2 entry in a std. priv-ld. -    NewTupleList=lists:keydelete(CallingPid,1,TupleList), % If present, remove previous call. -    {ok, -     {setelement(1,Part1,[{CallingPid,{Alias,Pid},TS}|NewTupleList]),GlobalData}, -     void}. - -%% Return-function for the erlang:register/2 BIF. -%% This function formulates the output and removes the corresponding call entry -%% from the standard priv-ld structure. -local_register_return(CallingPid,{return_from,_Val,_TS},PublLD={Part1,GlobalData}) -> -    TupleList=element(1,Part1),             % The register/2 entry in a std. priv-ld. -    case lists:keysearch(CallingPid,1,TupleList) of -	{value,{_,{Alias,Pid},NowTS}} -> -	    NewTupleList=lists:keydelete(CallingPid,1,TupleList), -	    {ok, -	     {setelement(1,Part1,NewTupleList),GlobalData}, -	     term_to_binary({Pid,Alias,alias,NowTS})}; -	false ->                            % Strange, then don't know what to do. -	    {ok,PublLD,void}                % Do nothing seems safe. -    end; -local_register_return(CallingPid,{exception_from,_Val,_TS},{Part1,GlobalData}) -> -    TupleList=element(1,Part1),             % The register/2 entry in a std. priv-ld. -    NewTupleList=lists:keydelete(CallingPid,1,TupleList), -    {ok,{setelement(1,Part1,NewTupleList),GlobalData},void}; % No association then. -local_register_return(_,_,PublLD) ->        % Don't understand this. -    {ok,PublLD,void}. - -%% When unregister/1 us called we simply want a unalias entry in the ti-file. -%% We can unfortunately not connect it with a certain pid. -local_unregister_call(_CallingPid,{_TypeTag,[Alias],TS},PublLD) -> -    {ok,PublLD,term_to_binary({undefined,Alias,unalias,TS})}. -%% ----------------------------------------------------------------------------- - -%% Call-function for global:register_name/2,/3. -%% This function is actually the call function for the handle_call/3 in the -%% global server. Note that we must check that we only do this on the node -%% where Pid actually resides. -global_register_call(_CallingPid,{call,[{register,Alias,P,_},_,_],TS},PublLD) -  when node(P)==node()-> -    {ok,PublLD,term_to_binary({P,{global,Alias},alias,TS})}; -global_register_call(_CallingPid,_,PublLD) -> -    {ok,PublLD,void}. - -%% Call-function for global:unregister_name. It acutally checks on the use of -%% global:delete_global_name/2 which is called when ever a global name is removed. -global_unregister_call(_CallingPid,{call,[Alias,P],TS},PublLD) when node(P)==node()-> -    {ok,PublLD,term_to_binary({P,{global,Alias},unalias,TS})}; -global_unregister_call(_CallingPid,_,PublLD) -> -    {ok,PublLD,void}. -%% ----------------------------------------------------------------------------- - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src index 1152f7259d..60be9ed7c9 100644 --- a/lib/runtime_tools/src/runtime_tools.app.src +++ b/lib/runtime_tools/src/runtime_tools.app.src @@ -17,16 +17,13 @@  %% %CopyrightEnd%  %%  {application, runtime_tools, -   [{description,  "RUNTIME_TOOLS version 1"}, +   [{description,  "RUNTIME_TOOLS"},      {vsn,          "%VSN%"},      {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,  		    ttb_autostart,dyntrace]}, -    {registered,   [runtime_tools_sup,inviso_rt,inviso_rt_meta]}, +    {registered,   [runtime_tools_sup]},      {applications, [kernel, stdlib]}, -%    {env,          [{inviso_autostart_mod,your_own_autostart_module}]},      {env,          []},      {mod,          {runtime_tools, []}}]}. diff --git a/lib/runtime_tools/src/runtime_tools_sup.erl b/lib/runtime_tools/src/runtime_tools_sup.erl index 913719c449..c8dab250dd 100644 --- a/lib/runtime_tools/src/runtime_tools_sup.erl +++ b/lib/runtime_tools/src/runtime_tools_sup.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %%  -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -31,15 +31,11 @@  %% =============================================================================  %% The runtime tools top most supervisor starts: -%% -The inviso runtime component. This is the only way to get the runtime component -%%  started automatically (if for instance autostart is wanted). -%%  Note that it is not impossible that the runtime component terminates it self -%%  should it discover that no autostart is configured. +%% -The ttb_autostart component. This is used for tracing at startup +%%  using observer/ttb.  init(AutoModArgs) ->      Flags = {one_for_one, 0, 3600}, -    Children = [{inviso_rt, {inviso_rt, start_link_auto, [AutoModArgs]},  -		 temporary, 3000, worker, [inviso_rt]}, -                {ttb_autostart, {ttb_autostart, start_link, []}, +    Children = [{ttb_autostart, {ttb_autostart, start_link, []},                   temporary, 3000, worker, [ttb_autostart]}],      {ok, {Flags, Children}}.  %% ----------------------------------------------------------------------------- | 
