From f20e2aaaf1aba9c545a3687a8524b4ab3b5b52c4 Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Mon, 7 Jun 2021 00:15:17 +0800 Subject: [PATCH] =?UTF-8?q?ft:=20=E4=BB=A3=E7=A0=81=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- c_src/tracer/tpTracerNif.c | 4 +- src/flame/tpFlame.erl | 251 +++++++++++++++++-------------------- 2 files changed, 114 insertions(+), 141 deletions(-) diff --git a/c_src/tracer/tpTracerNif.c b/c_src/tracer/tpTracerNif.c index 862a813..f3d1edc 100644 --- a/c_src/tracer/tpTracerNif.c +++ b/c_src/tracer/tpTracerNif.c @@ -222,7 +222,7 @@ static ERL_NIF_TERM trace(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { return atom_ok; } -static ErlNifFunc nifFuns[] = { +static ErlNifFunc nif_funs[] = { {"enabled", 3, enabled}, {"enabled_call", 3, enabled_call}, {"enabled_procs", 3, enabled_procs}, @@ -233,4 +233,4 @@ static ErlNifFunc nifFuns[] = { {"trace", 5, trace}, }; -ERL_NIF_INIT(tpTracerNif, nifFuns, load, NULL, upgrade, unload) \ No newline at end of file +ERL_NIF_INIT(tpTracerNif, nif_funs, load, NULL, upgrade, unload) \ No newline at end of file diff --git a/src/flame/tpFlame.erl b/src/flame/tpFlame.erl index 10ba101..3f39049 100644 --- a/src/flame/tpFlame.erl +++ b/src/flame/tpFlame.erl @@ -1,8 +1,8 @@ --module(tpFlame). +-module(tpFlame). %% 火焰图分析 -export([ - pfs/2 %% 分析单个文件 - , pfm/2 %% 分析多个文件 + pfs/2 %% 分析单个文件 + , pfm/3 %% 分析多个文件 ]). -record(state, { @@ -18,9 +18,9 @@ pfs(InputFile, OutputPath) -> {ok, FinalState} = tpFReader:fold(fun handleEvent/2, #state{outputPath = OutputPath}, InputFile), flush(FinalState). --spec pfm(file:filename(), file:filename()) -> ok. -pfm(InputFiles, OutputPath) -> - PfFiles = filelib:wildcard(InputFiles), +-spec pfm(file:filename(), filelib:dirname(), file:filename()) -> ok. +pfm(InputFiles, Cwd, OutputPath) -> + PfFiles = filelib:wildcard(InputFiles, Cwd), doPfm(PfFiles, #state{outputPath = OutputPath}). doPfm([], State) -> @@ -29,65 +29,46 @@ doPfm([InputFile | PfFiles], State) -> {ok, NewState} = tpFReader:fold(fun handleEvent/2, State, InputFile), doPfm(PfFiles, NewState). -handleEvent({Type, Pid, Ts, Arg}, State) -> - doExp({trace_ts, Pid, Type, Arg, Ts}, State); -handleEvent({Type, Pid, Ts, Arg, ExtraOrMspec}, State) -> - doExp({trace_ts, Pid, Type, Arg, ExtraOrMspec, Ts}, State); -handleEvent({Type, Pid, Ts, Arg, Extra, Mspec}, State) -> - doExp({trace_ts, Pid, Type, Arg, Extra, Mspec, Ts}, State). - -doExp(T, #state{outputPath = OutputPath} = State) -> - trace_ts = element(1, T), - Pid = element(2, T), - PidState = - case erlang:get(Pid) of - undefined -> - io:format("~p ", [Pid]), - #state{outputPath = OutputPath}; - SomeState -> - SomeState - end, - NewPidState = doExpInner(T, PidState), - erlang:put(Pid, NewPidState), +handleEvent({_TraceTag, _Pid, _Ts, _Arg} = Trace, State) -> + Pid = element(2, Trace), + PidState = getPidState(Pid), + NewPidState = doExpInner(Trace, PidState), + setPidState(Pid, NewPidState), State. %% in & out, without call context, don't help us -doExpInner({trace_ts, _Pid, InOut, _MFA, _TS}, #state{lastTs = undefined} = PS) when InOut == in; InOut == out -> +doExpInner({InOut, _Pid, _TS, _MFA}, #state{lastTs = undefined} = PS) when InOut == in; InOut == out -> PS; %% return_from and return_to, without call context, don't help us -doExpInner({trace_ts, _Pid, Return, _MFA, _TS}, #state{lastTs = undefined} = PS) when Return == return_from; Return == return_to -> +doExpInner({Return, _Pid, _TS, _MFA}, #state{lastTs = undefined} = PS) when Return == return_from; Return == return_to -> PS; -doExpInner({trace_ts, Pid, call, MFA, BIN, TS}, #state{lastTs = LastTS, acc = Acc, count = Count} = PS) -> +doExpInner({call, Pid, TS, MFA, BIN}, #state{lastTs = LastTS, acc = Acc, count = Count} = PS) -> try - %% Calculate time elapsed, TS-LastTs. - %% 0. If Acc is empty, then skip step #1. - %% 1. Credit elapsed time to the stack on the top of Acc. - %% 2. Push a 0 usec item with this stack onto Acc. - Stak = - lists:filter( - fun(<<"unknown function">>) -> false; - (_) -> true - end, stak_binify(BIN)), - Stack0 = stak_trim(Stak), - MFA_bin = mfa_binify(MFA), - Stack1 = [MFA_bin | lists:reverse(Stack0)], - Acc2 = + %% Calculate time elapsed, TS-LastTs. 计算经过的时间,TS-LastTs。 + %% 0. If Acc is empty, then skip step #1. 0. 如果 Acc 为空,则跳过步骤 1。 + %% 1. Credit elapsed time to the stack on the top of Acc. 1. 将经过的时间计入 Acc 顶部的堆栈。 + %% 2. Push a 0 usec item with this stack onto Acc. 2. 将带有此堆栈的 0 usec 项目推送到 Acc。 + + Stack = lists:filter(fun(<<"unknown function">>) -> false;(_) -> true end, stackToBin(BIN)), + TrimStack = stackTrim(Stack), + MFABin = mfaToBin(MFA), + NewStack = [MFABin | lists:reverse(TrimStack)], + NewAcc = case Acc of [] -> - [{Stack1, 0}]; + [{NewStack, 0}]; [{LastStack, LastTime} | Tail] -> USec = TS - LastTS, -% io:format("Stack1: ~p ~p\n", [Stack1, USec]), - [{Stack1, 0}, - {LastStack, LastTime + USec} | Tail] + % io:format("Stack1: ~p ~p\n", [Stack1, USec]), + [{NewStack, 0}, {LastStack, LastTime + USec} | Tail] end, %% TODO: more state tracking here. - PS#state{pid = Pid, lastTs = TS, count = Count + 1, acc = Acc2} + PS#state{pid = Pid, lastTs = TS, count = Count + 1, acc = NewAcc} catch Class:Reason:StackTrace -> io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, Class, Reason, StackTrace]), PS end; -doExpInner({trace_ts, _Pid, return_to, MFA, TS}, #state{lastTs = LastTS, acc = Acc} = S) -> +doExpInner({return_to, _Pid, TS, MFA}, #state{lastTs = LastTS, acc = Acc} = PS) -> try %% Calculate time elapsed, TS-LastTs. %% 1. Credit elapsed time to the stack on the top of Acc. @@ -95,108 +76,89 @@ doExpInner({trace_ts, _Pid, return_to, MFA, TS}, #state{lastTs = LastTS, acc = A %% "best" = MFA exists in the middle of the stack onto Acc, %% or else MFA exists at the top of a stack elsewhere in Acc. [{LastStack, LastTime} | Tail] = Acc, - MFA_bin = mfa_binify(MFA), - BestStack = lists:dropwhile(fun(SomeMFA) when SomeMFA /= MFA_bin -> true; - (_) -> false - end, find_matching_stack(MFA_bin, Acc)), + MFABin = mfaToBin(MFA), + BestStack = lists:dropwhile(fun(SomeMFA) when SomeMFA /= MFABin -> true;(_) -> false end, findMatchingStack(MFABin, Acc)), USec = TS - LastTS, - Acc2 = [{BestStack, 0}, - {LastStack, LastTime + USec} | Tail], -% io:format(user, "return-to: ~p\n", [lists:sublist(Acc2, 4)]), - S#state{lastTs = TS, acc = Acc2} - catch XX:YY:ZZ -> - io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]), - S + NewAcc = [{BestStack, 0}, {LastStack, LastTime + USec} | Tail], + % io:format(user, "return-to: ~p\n", [lists:sublist(Acc2, 4)]), + PS#state{lastTs = TS, acc = NewAcc} + catch Class:Reason:StackTrace -> + io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, Class, Reason, StackTrace]), + PS end; - -doExpInner({trace_ts, _Pid, gc_start, _Info, TS}, #state{lastTs = LastTS, acc = Acc} = S) -> +doExpInner({gc_start, _Pid, TS, _Info}, #state{lastTs = LastTS, acc = Acc} = PS) -> try %% Push a 0 usec item onto Acc. [{LastStack, LastTime} | Tail] = Acc, NewStack = [<<"GARBAGE-COLLECTION">> | LastStack], USec = TS - LastTS, - Acc2 = [{NewStack, 0}, - {LastStack, LastTime + USec} | Tail], -% io:format(user, "GC 1: ~p\n", [lists:sublist(Acc2, 4)]), - S#state{lastTs = TS, acc = Acc2} - catch _XX:_YY:_ZZ -> - %% io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, _XX, _YY, _ZZ]), - S + NewAcc = [{NewStack, 0}, {LastStack, LastTime + USec} | Tail], + % io:format(user, "GC 1: ~p\n", [lists:sublist(Acc2, 4)]), + PS#state{lastTs = TS, acc = NewAcc} + catch Class:Reason:StackTrace -> + io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, Class, Reason, StackTrace]), + PS end; -doExpInner({trace_ts, _Pid, gc_end, _Info, TS}, #state{lastTs = LastTS, acc = Acc} = S) -> +doExpInner({gc_end, _Pid, TS, _Info}, #state{lastTs = LastTS, acc = Acc} = PS) -> try %% Push the GC time onto Acc, then push 0 usec item from last exec %% stack onto Acc. [{GCStack, GCTime}, {LastExecStack, _} | Tail] = Acc, USec = TS - LastTS, - Acc2 = [{LastExecStack, 0}, {GCStack, GCTime + USec} | Tail], -% io:format(user, "GC 2: ~p\n", [lists:sublist(Acc2, 4)]), - S#state{lastTs = TS, acc = Acc2} - catch _XX:_YY:_ZZ -> - %% io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, _XX, _YY, _ZZ]), - S + NewAcc = [{LastExecStack, 0}, {GCStack, GCTime + USec} | Tail], + % io:format(user, "GC 2: ~p\n", [lists:sublist(Acc2, 4)]), + PS#state{lastTs = TS, acc = NewAcc} + catch Class:Reason:StackTrace -> + io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, Class, Reason, StackTrace]), + PS end; - -doExpInner({trace_ts, _Pid, out, MFA, TS}, #state{lastTs = LastTS, acc = Acc} = S) -> +doExpInner({out, _Pid, TS, MFA}, #state{lastTs = LastTS, acc = Acc} = PS) -> try %% Push a 0 usec item onto Acc. %% The MFA reported here probably doesn't appear in the stacktrace %% given to us by the last 'call', so add it here. [{LastStack, LastTime} | Tail] = Acc, - MFA_bin = mfa_binify(MFA), + MFA_bin = mfaToBin(MFA), NewStack = [<<"SLEEP">>, MFA_bin | LastStack], USec = TS - LastTS, - Acc2 = [{NewStack, 0}, - {LastStack, LastTime + USec} | Tail], - S#state{lastTs = TS, acc = Acc2} - catch XX:YY:ZZ -> - io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]), - S + NewAcc = [{NewStack, 0}, {LastStack, LastTime + USec} | Tail], + PS#state{lastTs = TS, acc = NewAcc} + catch Class:Reason:StackTrace -> + io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, Class, Reason, StackTrace]), + PS end; -doExpInner({trace_ts, _Pid, in, MFA, TS}, #state{lastTs = LastTS, acc = Acc} = S) -> +doExpInner({in, _Pid, TS, MFA}, #state{lastTs = LastTS, acc = Acc} = PS) -> try %% Push the Sleep time onto Acc, then push 0 usec item from last %% exec stack onto Acc. %% The MFA reported here probably doesn't appear in the stacktrace %% given to us by the last 'call', so add it here. - MFA_bin = mfa_binify(MFA), + MFA_bin = mfaToBin(MFA), [{SleepStack, SleepTime}, {LastExecStack, _} | Tail] = Acc, USec = TS - LastTS, - Acc2 = [{[MFA_bin | LastExecStack], 0}, {SleepStack, SleepTime + USec} | Tail], - S#state{lastTs = TS, acc = Acc2} - catch XX:YY:ZZ -> - io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]), - S + NewAcc = [{[MFA_bin | LastExecStack], 0}, {SleepStack, SleepTime + USec} | Tail], + PS#state{lastTs = TS, acc = NewAcc} + catch Class:Reason:StackTrace -> + io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, Class, Reason, StackTrace]), + PS end; +doExpInner(_Else, PS) -> + % io:format("?? ~P\n", [_Else, 10]), + PS. -%exp1_inner(end_of_trace = _Else, #state{pid=Pid, output_path=OutputPath, acc=Acc} = S) -> -% {ok, FH} = file:open(OutputPath, [write, raw, binary, delayed_write]), -% io:format("Writing to ~s ... ", [OutputPath]), -% [begin -% Pid_str = io_lib:format("~w", [Pid]), -% Time_str = integer_to_list(Time), -% file:write(FH, [Pid_str, $;, intersperse($;, lists:reverse(Stack)), 32, Time_str, 10]) -% end || {Stack, Time} <- Acc], -% file:close(FH), -% io:format("finished\n"), -% S; -doExpInner(_Else, S) -> -% io:format("?? ~P\n", [_Else, 10]), - S. - -find_matching_stack(MFA_bin, [{H, _Time} | _] = Acc) -> - case lists:member(MFA_bin, H) of +findMatchingStack(MFABin, [{H, _Time} | _] = Acc) -> + case lists:member(MFABin, H) of true -> H; - false -> - find_matching_stack2(MFA_bin, Acc) + _ -> + forFindMatchingStack(MFABin, Acc) end. -find_matching_stack2(MFA_bin, [{[MFA_bin | _StackTail] = Stack, _Time} | _]) -> +forFindMatchingStack(MFABin, [{[MFABin | _StackTail] = Stack, _Time} | _]) -> Stack; -find_matching_stack2(MFA_bin, [_H | T]) -> - find_matching_stack2(MFA_bin, T); -find_matching_stack2(_MFA_bin, []) -> +forFindMatchingStack(MFABin, [_H | T]) -> + forFindMatchingStack(MFABin, T); +forFindMatchingStack(_MFABin, []) -> [<<"FIND-MATCHING-FAILED">>]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -205,26 +167,25 @@ intersperse(_, []) -> []; intersperse(_, [X]) -> [X]; intersperse(Sep, [X | Xs]) -> [X, Sep | intersperse(Sep, Xs)]. -stak_trim([<<"proc_lib:init_p_do_apply/3">>, <<"gen_fsm:decode_msg/9">>, <<"gen_fsm:handle_msg/7">>, <<"gen_fsm:loop/7">> | T]) -> - stak_trim([<<"GEN-FSM">> | T]); -stak_trim([<<"GEN-FSM">>, <<"gen_fsm:decode_msg/9">>, <<"gen_fsm:handle_msg/7">>, <<"gen_fsm:loop/7">> | T]) -> - stak_trim([<<"GEN-FSM">> | T]); -stak_trim(Else) -> +stackTrim([<<"proc_lib:init_p_do_apply/3">>, <<"gen_fsm:decode_msg/9">>, <<"gen_fsm:handle_msg/7">>, <<"gen_fsm:loop/7">> | T]) -> + stackTrim([<<"GEN-FSM">> | T]); +stackTrim([<<"GEN-FSM">>, <<"gen_fsm:decode_msg/9">>, <<"gen_fsm:handle_msg/7">>, <<"gen_fsm:loop/7">> | T]) -> + stackTrim([<<"GEN-FSM">> | T]); +stackTrim(Else) -> Else. -stak_binify(Bin) when is_binary(Bin) -> - [list_to_binary(X) || X <- stak(Bin)]; -stak_binify(X) -> - list_to_binary(io_lib:format("~w", [X])). +stackToBin(Bin) when is_binary(Bin) -> + [list_to_binary(X) || X <- stack(Bin)]; +stackToBin(X) -> + eFmt:formatBin("~w", [X]). -mfa_binify({M, F, A}) -> - list_to_binary(io_lib:format("~w:~w/~w", [M, F, A])); -mfa_binify(X) -> - list_to_binary(io_lib:format("~w", [X])). +mfaToBin({M, F, A}) -> + eFmt:formatBin("~w:~w/~w", [M, F, A]); +mfaToBin(X) -> + eFmt:formatBin("~w", [X]). %% Borrowed from redbug.erl - -stak(Bin) -> +stack(Bin) -> lists:foldl(fun munge/2, [], string:tokens(binary_to_list(Bin), "\n")). munge(I, Out) -> @@ -253,16 +214,28 @@ flush(#state{outputPath = OutputPath}) -> PidStates = get(), {ok, FH} = file:open(OutputPath, [write, raw, binary, delayed_write]), io:format("\n\nWriting to ~s for ~w processes... ", [OutputPath, length(PidStates)]), - _ = [ - [begin - Pid_str0 = lists:flatten(io_lib:format("~w", [Pid])), - Size = length(Pid_str0), - Pid_str = [$(, lists:sublist(Pid_str0, 2, Size - 2), $)], - Time_str = integer_to_list(Time), - file:write(FH, [Pid_str, $;, intersperse($;, lists:reverse(Stack)), 32, Time_str, 10]) - end || {Stack, Time} <- Acc] - || {Pid, #state{acc = Acc} = _S} <- PidStates], - _ = file:close(FH), + [ + [ + begin + PidStr = eFmt:formatBin("~w", [Pid]), + Size = byte_size(PidStr), + TimeStr = integer_to_binary(Time), + file:write(FH, [<<"(">>, binary:part(PidStr, 1, Size -2) , <<")">>, $;, intersperse($;, lists:reverse(Stack)), 32, TimeStr, 10]) + end || {Stack, Time} <- Acc + ] || {Pid, #state{acc = Acc} = _PS} <- PidStates], + ok = file:close(FH), io:format("finished!\n"), ok. +setPidState(Pid, PidState) -> + erlang:put(Pid, PidState). + +getPidState(Pid) -> + case erlang:get(Pid) of + undefined -> + io:format("~p ", [Pid]), + #state{}; + SomeState -> + SomeState + end. +