%% ``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 via the world wide web 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.
%% 
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%% 
%%     $Id$
%%
%% Author: Lennart Öhman, lennart.ohman@st.se
%%
%% 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 atom(Mod),atom(Func),integer(Arity),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 atom(Mod),atom(Func),integer(Arity),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 atom(Mod),atom(Func),integer(Arity),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 atom(Mod),atom(Func),integer(Arity),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 atom(Mod),atom(Func),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 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 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 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 atom(ToNode),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 atom(M),atom(F),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 atom(M),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 atom(M),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 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 atom(M),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 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 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}.
%% -----------------------------------------------------------------------------

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
