aboutsummaryrefslogtreecommitdiffstats
path: root/lib/runtime_tools/src/inviso_rt_meta.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/runtime_tools/src/inviso_rt_meta.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/runtime_tools/src/inviso_rt_meta.erl')
-rw-r--r--lib/runtime_tools/src/inviso_rt_meta.erl1207
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}.
+%% -----------------------------------------------------------------------------
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+