%%%------------------------------------------------------------------- %%% File : sherk_prof.erl %%% Author : Mats Cronqvist %%% Description : %%% %%% Created : 30 Aug 2006 by Mats Cronqvist %%%------------------------------------------------------------------- -module(sherk_prof). -export([go/3]). -include("log.hrl"). go(Msg, Seq, initial) -> go(Msg, Seq, init()); go(end_of_trace, Seq, State) -> terminate(Seq,State), State; go(Msg, Seq, State) -> handler(Msg) ! {msg,Seq,Msg}, State. init() -> ?log([{starting,?MODULE}]), sherk_ets:new(sherk_prof), {start,now()}. terminate(Seq,{start,Start}) -> ?log([{finishing,sherk_prof}, {seq,Seq}, {time,timer:now_diff(now(),Start)/1000000}, {procs,length(ets:match(sherk_prof,{{handler,'$1'},'_'}))}]), TermFun = fun({{handler,_},Pid},_) -> Pid ! quit; (_,_) -> ok end, ets:foldl(TermFun,[],sherk_prof). handler({spawn,_,{Pid,_},_,_}) -> assert_handler(Pid); handler({_,{Pid,_},_,_}) -> assert_handler(Pid). assert_handler(Pid) -> case sherk_ets:lup(sherk_prof,{handler,Pid}) of [] -> P = spawn_link(fun handler/0), P ! {pid,Pid}, ets:insert(sherk_prof,{{handler,Pid},P}), P; HandlerPid -> HandlerPid end. -record(s,{gc=no,fd=no,in=no,ts,pid,stack=[]}). handler() -> receive {pid,Pid} -> hloop(#s{pid=Pid}); quit -> ok end. hloop(S) -> receive {msg,_Seq,{Tag,_,Info,TS}} -> hloop(hand(Tag,Info,TS,S)); quit -> ok end. hand(Tag,Info,TS,S) -> %%io:fwrite("~p ~p~n~p~n",[Tag,Info,S#s.stack]), case {Tag,Info} of {out,0} -> (out(Tag,S,TS))#s{fd=yes}; {in,0} -> in(Tag,S#s{fd=no},TS); {gc_start,_} -> (out(Tag,S,TS))#s{gc=yes}; {gc_end,_} -> in(Tag,S#s{gc=no},TS); {out,MFA} -> (out(Tag,stk(S,arity(MFA)),TS))#s{in=no}; {in,MFA} -> in(Tag,stk(S#s{in=yes},arity(MFA)),TS); {call,MFA} -> call(S,TS,arity(MFA)); {return_to,undefined} -> S; {return_to,MFA} -> retu(S,TS,arity(MFA)); {spawn,{_,MFA}} -> spwn(S,TS,arity(MFA)); {exit,_} -> exIt(S,TS); {_,_} -> S end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% out(Tag,S,TS) -> %% left MFA at top of stack upd({{total,sched},Tag}), upd({{pid,sched},S#s.pid,Tag}), case is_running(S) of false -> ?log([not_running,{tag,Tag},{state,S}]), S; true -> case S#s.ts of undefined -> ?log([no_time,{state,S}]); _ -> T = timer:now_diff(TS,S#s.ts), MFA = hd(S#s.stack), upd({total,time}, T), upd({{pid,time}, S#s.pid}, T), upd({{func,time}, MFA}, T), upd({{func,time}, S#s.pid, MFA}, T), upd({{stack,time}, S#s.pid, S#s.stack}, T) end, S#s{ts=TS} end. in(Tag,S,TS) -> %% entered MFA at top of stack upd({{total,sched},Tag}), upd({{pid,sched},S#s.pid,Tag}), case is_running(S) of false -> ?log([not_running,{tag,Tag},{state,S}]),S; true -> S#s{ts=TS} end. call(S,TS,MFA) -> in(call,push_stack(MFA, out(call,S,TS)),TS). retu(S,TS,MFA) -> in(return,pop_stack(MFA, out(return,S,TS)),TS). spwn(S,_TS,MFA) -> push_stack(MFA, S). exIt(S,TS) -> out(exit,S,TS). push_stack(MFA,S) -> case S#s.stack of [MFA|_] -> S; Stack -> Trunced = truncate([MFA|Stack]), upd({{func, calls}, MFA}), upd({{func, calls}, S#s.pid, MFA}), upd({{stack, calls}, S#s.pid, Trunced}), S#s{stack=Trunced} end. pop_stack(MFA,S) -> case lists:member(MFA,S#s.stack) of false -> ?log([dropped_headless_stack,{mfa,MFA},{state,S}]), erase_bad_stack(S), S#s{stack=[MFA]}; true -> S#s{stack=lists:dropwhile(fun(Mfa) -> MFA =/= Mfa end, S#s.stack)} end. truncate([MFA|Stack] = X) -> case lists:member(MFA,Stack) of true -> {H,[MFA|T]} = lists:splitwith(fun(E)->E/=MFA end,Stack), case lists:prefix(H,T) of true -> [MFA|T]; false -> X end; false -> X end. erase_bad_stack(S) -> mdl({{{stack,calls}, S#s.pid, '_'},'_'}), mdl({{{stack,time}, S#s.pid, '_'}, '_'}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% stk(S,MFA) -> case S#s.stack of [] -> S#s{stack=[arity(MFA)]}; _ -> S end. arity({M,F,A}) when is_list(A) -> {M,F,length(A)}; arity(MFA) -> MFA. is_running(#s{gc=no,fd=no,in=yes}) -> true; is_running(_) -> false. upd(Key) -> upd(Key,1). upd(Key,Inc) -> sherk_ets:upd(sherk_prof,Key,Inc). mdl(Pat) -> ets:match_delete(sherk_prof,Pat).