%%%------------------------------------------------------------------- %%% File : rdbg.erl %%% Author : Mats Cronqvist %%% Description : %%% %%% Created : 16 Aug 2004 by Mats Cronqvist %%%------------------------------------------------------------------- -module(rdbg). -export([rdbg/4,rdbg/0]). rdbg(Time,Msgs,Proc,Trc) -> (rdbg())(Time,Msgs,Proc,Trc). rdbg() -> Pi = fun(P) when pid(P) -> case process_info(P, registered_name) of [] -> case process_info(P, initial_call) of {_, {proc_lib,init_p,5}} -> proc_lib:translate_initial_call(P); {_,MFA} -> MFA; undefined -> unknown end; {_,Nam} -> Nam; undefined -> unknown end; (P) when port(P) -> {name,N} = erlang:port_info(P,name), [Hd|_] = string:tokens(N," "), Tl = lists:reverse(hd(string:tokens(lists:reverse(Hd),"/"))), list_to_atom(Tl); (R) when atom(R) -> R; ({R,Node}) when atom(R), Node == node() -> R; ({R, Node}) when atom(R), atom(Node) -> {R, Node} end, Ts = fun(Nw) -> {_,{H,M,S}} = calendar:now_to_local_time(Nw), {H,M,S,element(3,Nw)} end, Munge = fun(I) -> case string:str(I, "Return addr") of 0 -> case string:str(I, "cp = ") of 0 -> []; _ -> [_, C|_] = string:tokens(I,"()+"), list_to_atom(C) end; _ -> case string:str(I, "erminate process normal") of 0 -> [_, C|_] = string:tokens(I,"()+"), list_to_atom(C); _ -> [] end end end, Stack = fun(Bin) -> L = string:tokens(binary_to_list(Bin),"\n"), {stack,lists:flatten(lists:map(Munge,L))} end, Prc = fun(all) -> all; (Pd) when pid(Pd) -> Pd; ({pid,P1,P2}) when integer(P1), integer(P2) -> c:pid(0,P1,P2); (Reg) when atom(Reg) -> case whereis(Reg) of undefined -> exit({rdbg, no_such_process, Reg}); Pid when pid(Pid) -> Pid end end, MsF = fun(stack, [{Head,Cond,Body}]) -> [{Head,Cond,[{message,{process_dump}}|Body]}]; (return, [{Head,Cond,Body}]) -> [{Head,Cond,[{return_trace}|Body]}]; (Head, [{_,Cond,Body}]) when tuple(Head)-> [{Head,Cond,Body}]; (X,_) -> exit({rdbg,bad_match_spec,X}) end, Ms = fun(Mss) -> lists:foldl(MsF, [{'_',[],[]}], Mss) end, ChkTP = fun({M,F}) when atom(M), atom(F), M/='_', F/='_' -> {{M,F,'_'},[],[global]}; ({M,F,MS}) when atom(M), atom(F), M/='_', F/='_' -> {{M,F,'_'},Ms(MS),[global]}; ({M,F,MS,local}) when atom(M), atom(F), M/='_', F/='_' -> {{M,F,'_'},Ms(MS),[local]}; ({M,F,MS,global}) when atom(M), atom(F), M/='_', F/='_' -> {{M,F,'_'},Ms(MS),[global]}; (X) -> exit({rdbg,unrec_trace_pattern,X}) end, ChkTPs = fun(TPs) when list(TPs) -> lists:map(ChkTP,TPs); (TP) -> [ChkTP(TP)] end, SetTPs = fun({MFA,MS,Fs}) -> erlang:trace_pattern(MFA,MS,Fs) end, DoInitFun = fun(Time) -> erlang:register(rdbg, self()), erlang:start_timer(Time,self(),{die}), erlang:trace_pattern({'_','_','_'},false,[local]), erlang:trace_pattern({'_','_','_'},false,[global]) end, InitFun = fun(Time,all,send) -> exit({rdbg,too_many_processes}); (Time,all,'receive') -> exit({rdbg,too_many_processes}); (Time,P, send) -> DoInitFun(Time), erlang:trace(Prc(P),true,[send,timestamp]); (Time,P,'receive') -> DoInitFun(Time), erlang:trace(Prc(P),true,['receive',timestamp]); (Time,P,TPs) -> CTPs = ChkTPs(TPs), DoInitFun(Time), erlang:trace(Prc(P),true,[call,timestamp]), lists:foreach(SetTPs,CTPs) end, LoopFun = fun(G,N,Out) when N < 1 -> erlang:trace(all,false,[call,send,'receive']), erlang:trace_pattern({'_','_','_'},false,[local]), erlang:trace_pattern({'_','_','_'},false,[global]), io:fwrite("** rdbg, ~w msgs **~n", [length(Out)]), io:fwrite("~p~n",[lists:reverse(Out)]), io:fwrite("~p~n", [process_info(self(),message_queue_len)]); (G,Cnt,Out) -> case process_info(self(),message_queue_len) of {_,N} when N > 100 -> exit({rdbg,msg_queue,N}); _ -> ok end, receive {timeout,_,{die}} -> G(G,0,Out); {trace_ts,Pid,send,Msg,To,TS} -> G(G,Cnt-1,[{send,Ts(TS),Pi(To),Msg}|Out]); {trace_ts,Pid,'receive',Msg,TS} -> G(G,Cnt-1,[{'receive',Ts(TS),Msg}|Out]); {trace_ts,Pid,return_from,MFA,V,TS} -> G(G,Cnt-1,[{return,MFA,V}|Out]); {trace_ts,Pid,call,MFA,B,TS} when binary(B) -> G(G,Cnt-1,[{Pi(Pid),Ts(TS),{Stack(B),MFA}}|Out]); {trace_ts,Pid,call,MFA,TS} -> G(G,Cnt-1,[{Pi(Pid),Ts(TS),MFA}|Out]) end end, Rdbg = fun(Time,Msgs,Proc,Trc) when integer(Time), integer(Msgs) -> Start = fun() ->InitFun(Time,Proc,Trc),LoopFun(LoopFun,Msgs,[]) end, erlang:spawn_link(Start) end. %% Pi = fun(P) when pid(P) -> case process_info(P, registered_name) of [] -> case process_info(P, initial_call) of {_, {proc_lib,init_p,5}} -> proc_lib:translate_initial_call(P); {_,MFA} -> MFA; undefined -> unknown end; {_,Nam} -> Nam; undefined -> unknown end; (P) when port(P) -> {name,N} = erlang:port_info(P,name), [Hd|_] = string:tokens(N," "), Tl = lists:reverse(hd(string:tokens(lists:reverse(Hd),"/"))), list_to_atom(Tl); (R) when atom(R) -> R; ({R,Node}) when atom(R), Node == node() -> R; ({R, Node}) when atom(R), atom(Node) -> {R, Node} end, Ts = fun(Nw) -> {_,{H,M,S}} = calendar:now_to_local_time(Nw), {H,M,S,element(3,Nw)} end, Munge = fun(I) -> case string:str(I, "Return addr") of 0 -> case string:str(I, "cp = ") of 0 -> []; _ -> [_, C|_] = string:tokens(I,"()+"), list_to_atom(C) end; _ -> case string:str(I, "erminate process normal") of 0 -> [_, C|_] = string:tokens(I,"()+"), list_to_atom(C); _ -> [] end end end, Stack = fun(Bin) -> L = string:tokens(binary_to_list(Bin),"\n"), {stack,lists:flatten(lists:map(Munge,L))} end, Prc = fun(all) -> all; (Pd) when pid(Pd) -> Pd; ({pid,P1,P2}) when integer(P1), integer(P2) -> c:pid(0,P1,P2); (Reg) when atom(Reg) -> case whereis(Reg) of undefined -> exit({rdbg, no_such_process, Reg}); Pid when pid(Pid) -> Pid end end, MsF = fun(stack, [{Head,Cond,Body}]) -> [{Head,Cond,[{message,{process_dump}}|Body]}]; (return, [{Head,Cond,Body}]) -> [{Head,Cond,[{return_trace}|Body]}]; (Head, [{_,Cond,Body}]) when tuple(Head)-> [{Head,Cond,Body}]; (X,_) -> exit({rdbg,bad_match_spec,X}) end, Ms = fun(Mss) -> lists:foldl(MsF, [{'_',[],[]}], Mss) end, ChkTP = fun({M,F}) when atom(M), atom(F), M/='_', F/='_' -> {{M,F,'_'},[],[global]}; ({M,F,MS}) when atom(M), atom(F), M/='_', F/='_' -> {{M,F,'_'},Ms(MS),[global]}; ({M,F,MS,local}) when atom(M), atom(F), M/='_', F/='_' -> {{M,F,'_'},Ms(MS),[local]}; ({M,F,MS,global}) when atom(M), atom(F), M/='_', F/='_' -> {{M,F,'_'},Ms(MS),[global]}; (X) -> exit({rdbg,unrec_trace_pattern,X}) end, ChkTPs = fun(TPs) when list(TPs) -> lists:map(ChkTP,TPs); (TP) -> [ChkTP(TP)] end, SetTPs = fun({MFA,MS,Fs}) -> erlang:trace_pattern(MFA,MS,Fs) end, DoInitFun = fun(Time) -> erlang:register(rdbg, self()), erlang:start_timer(Time,self(),{die}), erlang:trace_pattern({'_','_','_'},false,[local]), erlang:trace_pattern({'_','_','_'},false,[global]) end, InitFun = fun(Time,all,send) -> exit({rdbg,too_many_processes}); (Time,all,'receive') -> exit({rdbg,too_many_processes}); (Time,P, send) -> DoInitFun(Time), erlang:trace(Prc(P),true,[send,timestamp]); (Time,P,'receive') -> DoInitFun(Time), erlang:trace(Prc(P),true,['receive',timestamp]); (Time,P,TPs) -> CTPs = ChkTPs(TPs), DoInitFun(Time), erlang:trace(Prc(P),true,[call,timestamp]), lists:foreach(SetTPs,CTPs) end, LoopFun = fun(G,N,Out) when N < 1 -> erlang:trace(all,false,[call,send,'receive']), erlang:trace_pattern({'_','_','_'},false,[local]), erlang:trace_pattern({'_','_','_'},false,[global]), io:fwrite("** rdbg, ~w msgs **~n", [length(Out)]), io:fwrite("~p~n",[lists:reverse(Out)]), io:fwrite("~p~n", [process_info(self(),message_queue_len)]); (G,Cnt,Out) -> case process_info(self(),message_queue_len) of {_,N} when N > 100 -> exit({rdbg,msg_queue,N}); _ -> ok end, receive {timeout,_,{die}} -> G(G,0,Out); {trace_ts,Pid,send,Msg,To,TS} -> G(G,Cnt-1,[{send,Ts(TS),Pi(To),Msg}|Out]); {trace_ts,Pid,'receive',Msg,TS} -> G(G,Cnt-1,[{'receive',Ts(TS),Msg}|Out]); {trace_ts,Pid,return_from,MFA,V,TS} -> G(G,Cnt-1,[{return,MFA,V}|Out]); {trace_ts,Pid,call,MFA,B,TS} when binary(B) -> G(G,Cnt-1,[{Pi(Pid),Ts(TS),{Stack(B),MFA}}|Out]); {trace_ts,Pid,call,MFA,TS} -> G(G,Cnt-1,[{Pi(Pid),Ts(TS),MFA}|Out]) end end, Rdbg = fun(Time,Msgs,Proc,Trc) when integer(Time), integer(Msgs) -> Start = fun() -> InitFun(Time,Proc,Trc),LoopFun(LoopFun,Msgs,[]) end, erlang:spawn_link(Start) end.