diff options
Diffstat (limited to 'lib/runtime_tools/src/inviso_rt_meta.erl')
-rw-r--r-- | lib/runtime_tools/src/inviso_rt_meta.erl | 1207 |
1 files changed, 1207 insertions, 0 deletions
diff --git a/lib/runtime_tools/src/inviso_rt_meta.erl b/lib/runtime_tools/src/inviso_rt_meta.erl new file mode 100644 index 0000000000..6865dc2242 --- /dev/null +++ b/lib/runtime_tools/src/inviso_rt_meta.erl @@ -0,0 +1,1207 @@ +%% +%% %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}. +%% ----------------------------------------------------------------------------- + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + |