diff options
Diffstat (limited to 'lib/runtime_tools/src/inviso_autostart_server.erl')
-rw-r--r-- | lib/runtime_tools/src/inviso_autostart_server.erl | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/lib/runtime_tools/src/inviso_autostart_server.erl b/lib/runtime_tools/src/inviso_autostart_server.erl new file mode 100644 index 0000000000..5af96e4e39 --- /dev/null +++ b/lib/runtime_tools/src/inviso_autostart_server.erl @@ -0,0 +1,311 @@ +%% +%% %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] +%% +-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,_} -> % 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([FileName|Rest],GlobalBindings,Translations,Dbg) -> + interpret_cmd_files_1(FileName,GlobalBindings,Translations,Dbg), + interpret_cmd_files(Rest,GlobalBindings,Translations,Dbg); +interpret_cmd_files([],_,_,_) -> % Done, return nothing significant! + true. + +%% 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 + + + |