Procházet zdrojové kódy

ft: 代码修改

master
SisMaker před 3 roky
rodič
revize
e52787bf3f
7 změnil soubory, kde provedl 295 přidání a 285 odebrání
  1. binární
      priv/tpTracerNif.so
  2. +1
    -1
      src/callgrind/tpCallGrind.erl
  3. +1
    -2
      src/fileHer/tpFReader.erl
  4. +209
    -215
      src/flame/tpFlame.erl
  5. +20
    -11
      src/messages/tpMsgRS.erl
  6. +59
    -54
      src/messages/tpMsgSD.erl
  7. +5
    -2
      src/tracer/tpTracerConsole.erl

binární
priv/tpTracerNif.so Zobrazit soubor


+ 1
- 1
src/callgrind/tpCallGrind.erl Zobrazit soubor

@ -77,7 +77,7 @@ profile(Input, Output, Opts) ->
{ok, OutDevice} = file:open(Output, [write]),
State = #state{input = Input, output = Output, output_device = OutDevice, opts = Opts},
write_header(State),
{ok, FinalState} = tpFileReader:fold(fun handle_event/2, State, Input),
{ok, FinalState} = tpFReader:fold(fun handle_event/2, State, Input),
flush(FinalState),
_ = file:close(OutDevice),
ok.

src/fileHer/tpFileReader.erl → src/fileHer/tpFReader.erl Zobrazit soubor

@ -1,4 +1,4 @@
-module(tpFileReader).
-module(tpFReader).
-export([
fold/3
@ -17,7 +17,6 @@
}).
%% High level API.
fold(Fun, Acc, Filename) ->
{ok, IoDevice} = open(Filename),
Ctx = lz4f:create_decompression_context(),

+ 209
- 215
src/flame/tpFlame.erl Zobrazit soubor

@ -1,197 +1,173 @@
-module(tpFlame).
-export([profile/2]).
-export([profile_many/2]).
-export([
pfs/2 %%
, pfm/2 %%
]).
-record(state, {
output_path = "",
pid,
last_ts,
count = 0,
acc = []}). % per-process state
outputPath = "",
pid,
lastTs,
count = 0,
acc = []
}).
-spec profile(file:filename_all(), file:filename_all()) -> ok.
profile(Input, Output) ->
InitialState = exp1_init(Output),
{ok, FinalState} = tpFileReader:fold(fun handle_event/2, InitialState, Input),
flush(FinalState).
-spec pfs(file:filename_all(), file:filename_all()) -> ok.
pfs(InputFile, OutputPath) ->
{ok, FinalState} = tpFReader:fold(fun handleEvent/2, #state{outputPath = OutputPath}, InputFile),
flush(FinalState).
-spec profile_many(file:filename(), file:filename()) -> ok.
profile_many(Wildcard, Output) ->
InitialState = exp1_init(Output),
Files = filelib:wildcard(Wildcard),
FinalState = lists:foldl(fun(Input, State0) ->
case tpFileReader:fold(fun handle_event/2, State0, Input) of
{ok, State} ->
State;
{error, Reason, HumanReadable} ->
io:format("Error ~p while reading ~s:~n~s~n",
[Reason, Input, HumanReadable]),
State0
end
end, InitialState, Files),
flush(FinalState).
-spec pfm(file:filename(), file:filename()) -> ok.
pfm(InputFiles, OutputPath) ->
PfFiles = filelib:wildcard(InputFiles),
doPfm(PfFiles, #state{outputPath = OutputPath}).
flush(#state{output_path = 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),
io:format("finished!\n"),
ok.
doPfm([], State) ->
flush(State);
doPfm([InputFile | PfFiles], State) ->
{ok, NewState} = tpFReader:fold(fun handleEvent/2, State, InputFile),
doPfm(PfFiles, NewState).
handle_event({Type, Pid, Ts, Arg}, State) ->
exp1({trace_ts, Pid, Type, Arg, Ts}, State);
handle_event({Type, Pid, Ts, Arg, ExtraOrMspec}, State) ->
exp1({trace_ts, Pid, Type, Arg, ExtraOrMspec, Ts}, State);
handle_event({Type, Pid, Ts, Arg, Extra, Mspec}, State) ->
exp1({trace_ts, Pid, Type, Arg, Extra, Mspec, Ts}, State).
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).
%% Below is Scott L. Fritchie's ISC licensed work with only a handful changes.
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),
State.
exp1_init(OutputPath) ->
#state{output_path = OutputPath}.
exp1(T, #state{output_path = OutputPath} = S) ->
trace_ts = element(1, T),
Pid = element(2, T),
PidState = case erlang:get(Pid) of
undefined ->
io:format("~p ", [Pid]),
#state{output_path = OutputPath};
SomeState ->
SomeState
end,
NewPidState = exp1_inner(T, PidState),
erlang:put(Pid, NewPidState),
S.
exp1_inner({trace_ts, _Pid, InOut, _MFA, _TS}, #state{last_ts = undefined} = S)
when InOut == in; InOut == out ->
%% in & out, without call context, don't help us
S;
exp1_inner({trace_ts, _Pid, Return, _MFA, _TS}, #state{last_ts = undefined} = S)
when Return == return_from; Return == return_to ->
%% return_from and return_to, without call context, don't help us
S;
exp1_inner({trace_ts, Pid, call, MFA, BIN, TS},
#state{last_ts = LastTS, acc = Acc, count = Count} = S) ->
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 = case Acc of
[] ->
[{Stack1, 0}];
[{LastStack, LastTime} | Tail] ->
USec = TS - LastTS,
%% 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 ->
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 ->
PS;
doExpInner({trace_ts, Pid, call, MFA, BIN, TS}, #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 =
case Acc of
[] ->
[{Stack1, 0}];
[{LastStack, LastTime} | Tail] ->
USec = TS - LastTS,
% io:format("Stack1: ~p ~p\n", [Stack1, USec]),
[{Stack1, 0},
{LastStack, LastTime + USec} | Tail]
end,
%% TODO: more state tracking here.
S#state{pid = Pid, last_ts = TS, count = Count + 1, acc = Acc2}
catch XX:YY:ZZ ->
io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]),
S
end;
exp1_inner({trace_ts, _Pid, return_to, MFA, TS}, #state{last_ts = LastTS, acc = Acc} = S) ->
try
%% Calculate time elapsed, TS-LastTs.
%% 1. Credit elapsed time to the stack on the top of Acc.
%% 2. Push a 0 usec item with the "best" stack onto Acc.
%% "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)),
USec = TS - LastTS,
Acc2 = [{BestStack, 0},
{LastStack, LastTime + USec} | Tail],
[{Stack1, 0},
{LastStack, LastTime + USec} | Tail]
end,
%% TODO: more state tracking here.
PS#state{pid = Pid, lastTs = TS, count = Count + 1, acc = Acc2}
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) ->
try
%% Calculate time elapsed, TS-LastTs.
%% 1. Credit elapsed time to the stack on the top of Acc.
%% 2. Push a 0 usec item with the "best" stack onto Acc.
%% "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)),
USec = TS - LastTS,
Acc2 = [{BestStack, 0},
{LastStack, LastTime + USec} | Tail],
% io:format(user, "return-to: ~p\n", [lists:sublist(Acc2, 4)]),
S#state{last_ts = TS, acc = Acc2}
catch XX:YY:ZZ ->
io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]),
S
end;
S#state{lastTs = TS, acc = Acc2}
catch XX:YY:ZZ ->
io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]),
S
end;
exp1_inner({trace_ts, _Pid, gc_start, _Info, TS}, #state{last_ts = LastTS, acc = Acc} = S) ->
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],
doExpInner({trace_ts, _Pid, gc_start, _Info, TS}, #state{lastTs = LastTS, acc = Acc} = S) ->
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{last_ts = TS, acc = Acc2}
catch _XX:_YY:_ZZ ->
%% io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, _XX, _YY, _ZZ]),
S
end;
exp1_inner({trace_ts, _Pid, gc_end, _Info, TS}, #state{last_ts = LastTS, acc = Acc} = S) ->
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],
S#state{lastTs = TS, acc = Acc2}
catch _XX:_YY:_ZZ ->
%% io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, _XX, _YY, _ZZ]),
S
end;
doExpInner({trace_ts, _Pid, gc_end, _Info, TS}, #state{lastTs = LastTS, acc = Acc} = S) ->
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{last_ts = TS, acc = Acc2}
catch _XX:_YY:_ZZ ->
%% io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, _XX, _YY, _ZZ]),
S
end;
S#state{lastTs = TS, acc = Acc2}
catch _XX:_YY:_ZZ ->
%% io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, _XX, _YY, _ZZ]),
S
end;
exp1_inner({trace_ts, _Pid, out, MFA, TS}, #state{last_ts = LastTS, acc = Acc} = S) ->
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),
NewStack = [<<"SLEEP">>, MFA_bin | LastStack],
USec = TS - LastTS,
Acc2 = [{NewStack, 0},
{LastStack, LastTime + USec} | Tail],
S#state{last_ts = TS, acc = Acc2}
catch XX:YY:ZZ ->
io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]),
S
end;
exp1_inner({trace_ts, _Pid, in, MFA, TS}, #state{last_ts = LastTS, acc = Acc} = S) ->
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),
[{SleepStack, SleepTime}, {LastExecStack, _} | Tail] = Acc,
USec = TS - LastTS,
Acc2 = [{[MFA_bin | LastExecStack], 0}, {SleepStack, SleepTime + USec} | Tail],
S#state{last_ts = TS, acc = Acc2}
catch XX:YY:ZZ ->
io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]),
S
end;
doExpInner({trace_ts, _Pid, out, MFA, TS}, #state{lastTs = LastTS, acc = Acc} = S) ->
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),
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
end;
doExpInner({trace_ts, _Pid, in, MFA, TS}, #state{lastTs = LastTS, acc = Acc} = S) ->
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),
[{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
end;
%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]),
@ -204,24 +180,24 @@ exp1_inner({trace_ts, _Pid, in, MFA, TS}, #state{last_ts = LastTS, acc = Acc} =
% file:close(FH),
% io:format("finished\n"),
% S;
exp1_inner(_Else, S) ->
doExpInner(_Else, S) ->
% io:format("?? ~P\n", [_Else, 10]),
S.
S.
find_matching_stack(MFA_bin, [{H, _Time} | _] = Acc) ->
case lists:member(MFA_bin, H) of
true ->
H;
false ->
find_matching_stack2(MFA_bin, Acc)
end.
case lists:member(MFA_bin, H) of
true ->
H;
false ->
find_matching_stack2(MFA_bin, Acc)
end.
find_matching_stack2(MFA_bin, [{[MFA_bin | _StackTail] = Stack, _Time} | _]) ->
Stack;
Stack;
find_matching_stack2(MFA_bin, [_H | T]) ->
find_matching_stack2(MFA_bin, T);
find_matching_stack2(MFA_bin, T);
find_matching_stack2(_MFA_bin, []) ->
[<<"FIND-MATCHING-FAILED">>].
[<<"FIND-MATCHING-FAILED">>].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -230,45 +206,63 @@ 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">> | 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([<<"GEN-FSM">> | T]);
stak_trim(Else) ->
Else.
Else.
stak_binify(Bin) when is_binary(Bin) ->
[list_to_binary(X) || X <- stak(Bin)];
[list_to_binary(X) || X <- stak(Bin)];
stak_binify(X) ->
list_to_binary(io_lib:format("~w", [X])).
list_to_binary(io_lib:format("~w", [X])).
mfa_binify({M, F, A}) ->
list_to_binary(io_lib:format("~w:~w/~w", [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])).
list_to_binary(io_lib:format("~w", [X])).
%% Borrowed from redbug.erl
stak(Bin) ->
lists:foldl(fun munge/2, [], string:tokens(binary_to_list(Bin), "\n")).
lists:foldl(fun munge/2, [], string:tokens(binary_to_list(Bin), "\n")).
munge(I, Out) ->
case I of %% lists:reverse(I) of
"..." ++ _ -> ["truncated!!!" | Out];
_ ->
case string:str(I, "Return addr") of
0 ->
case string:str(I, "cp = ") of
0 -> Out;
_ -> [mfaf(I) | Out]
end;
_ ->
case string:str(I, "erminate process normal") of
0 -> [mfaf(I) | Out];
_ -> Out
end
end
end.
case I of %% lists:reverse(I) of
"..." ++ _ -> ["truncated!!!" | Out];
_ ->
case string:str(I, "Return addr") of
0 ->
case string:str(I, "cp = ") of
0 -> Out;
_ -> [mfaf(I) | Out]
end;
_ ->
case string:str(I, "erminate process normal") of
0 -> [mfaf(I) | Out];
_ -> Out
end
end
end.
mfaf(I) ->
[_, C | _] = string:tokens(I, "()+"),
string:strip(C).
[_, C | _] = string:tokens(I, "()+"),
string:strip(C).
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),
io:format("finished!\n"),
ok.

+ 20
- 11
src/messages/tpMsgRS.erl Zobrazit soubor

@ -2,8 +2,10 @@
-include("eTpf.hrl").
-export([pfs/1]). %%
-export([pfm/1]). %%
-export([
pfs/1 %%
, pfm/1 %%
]).
-record(state, {
meta = #{} :: map(),
@ -16,7 +18,7 @@
-spec pfs(file:filename_all()) -> ok.
pfs(InputFile) ->
{ok, FinalState} = tpFileReader:fold(fun handleEvent/2, #state{}, InputFile),
{ok, FinalState} = tpFReader:fold(fun handleEvent/2, #state{}, InputFile),
flush(FinalState).
-spec pfm(file:filename()) -> ok.
@ -27,7 +29,7 @@ pfm(InputFiles) ->
doPfm([], State) ->
flush(State);
doPfm([InputFile | PfFiles], State) ->
{ok, NewState} = tpFileReader:fold(fun handleEvent/2, State, InputFile),
{ok, NewState} = tpFReader:fold(fun handleEvent/2, State, InputFile),
doPfm(PfFiles, NewState).
%% @todo Later we may want to look at the latency of gen_server call/reply.
@ -149,18 +151,18 @@ flushMostActivePairBidirectional(State = #state{pairs = Pairs}) ->
],
ok.
flushDigraph(State = #state{pairs = Pairs}) ->
flushDigraph(#state{pairs = Pairs} = State) ->
TemPairs = maps:fold(fun groupPairs/3, #{}, Pairs),
List = maps:to_list(TemPairs),
ok = file:write_file(<<"digraph.gv">>, [
HeaderBin =
<<"digraph {\n"
" concentrate=true;\n"
" splines=ortho;\n"
" edge [arrowhead=none, labelfontsize=12.0, minlen=3];\n"
"\n">>,
[eFmt:format(" \"~w~s\" -> \"~w~s\" [taillabel=~b, headlabel=~b];~n", [F, label(F, State), T, label(T, State), FC, TC]) || {{F, T}, {FC, TC}} <- List],
<<"}\n">>
]),
writeEvents(List, State, HeaderBin),
io:format(
"The file digraph.gv was created. Use GraphViz to make a PNG.~n"
"$ dot -Tpng -O digraph.gv~n"
@ -169,12 +171,19 @@ flushDigraph(State = #state{pairs = Pairs}) ->
"One line in the file is equal to a connection between two processes.~n"),
ok.
writeEvents([], _State, BinAcc) ->
LastBinAcc = <<BinAcc/binary, "}\n">>,
ok = file:write_file(<<"digraph.gv">>, LastBinAcc);
writeEvents([{{F, T}, {FC, TC}} | List], State, BinAcc) ->
EventBin = eFmt:formatBin(<<" \"~w~s\" -> \"~w~s\" [taillabel=~b, headlabel=~b];~n">>, [F, label(F, State), T, label(T, State), FC, TC]),
writeEvents(List, State, <<BinAcc/binary, EventBin/binary>>).
label(P, #state{meta = Meta}) ->
case maps:get(P, Meta, #{}) of
#{process_type := PT} ->
io_lib:format(" (~w)", [PT]);
eFmt:format(<<" (~w)";>>;, [PT]);
_ ->
""
<<"">>
end.
mergePairs({From, To}, Count, Acc) ->

+ 59
- 54
src/messages/tpMsgSD.erl Zobrazit soubor

@ -2,8 +2,10 @@
-include("eTpf.hrl").
-export([pfs/2]).
-export([pfm/2]).
-export([
pfs/2 %%
, pfm/2 %%
]).
-record(state, {
meta = #{} :: map(),
@ -13,7 +15,7 @@
-spec pfs(file:filename_all(), list()) -> ok.
pfs(InputFile, Pids) ->
{ok, FinalState} = tpFileReader:fold(fun handleEvent/2, #state{pids = preparePids(Pids)}, InputFile),
{ok, FinalState} = tpFReader:fold(fun handleEvent/2, #state{pids = preparePids(Pids)}, InputFile),
flush(FinalState).
-spec pfm(file:filename(), list()) -> ok.
@ -24,7 +26,7 @@ pfm(InputFiles, Pids) ->
doPfm([], State) ->
flush(State);
doPfm([InputFile | PfFiles], State) ->
{ok, NewState} = tpFileReader:fold(fun handleEvent/2, State, InputFile),
{ok, NewState} = tpFReader:fold(fun handleEvent/2, State, InputFile),
doPfm(PfFiles, NewState).
handleEvent({send, From, _, Info, ?eTpfHole}, State = #state{meta = Meta}) ->
@ -39,54 +41,56 @@ handleEvent({send, From, _, Info, ?eTpfHole}, State = #state{meta = Meta}) ->
handleEvent({send, From, _, _, To} = Event, State) ->
maybeKeepEvent(Event, From, To, State);
handleEvent({send_to_non_existing_process, From, _, _, To} = Event, State) ->
maybeKeepEvent(Event, From, To, State);
maybeKeepEvent(Event, From, To, State);
handleEvent({spawn, From, _, To, _} = Event, State) ->
maybeKeepEvent(Event, From, To, State);
handleEvent({exit, Pid0, _, _} = Event, State = #state{events = Events, pids = Pids}) ->
Pid = hide_pid_node(Pid0),
Pid = hidePidNode(Pid0),
case lists:member(Pid, Pids) of
true ->
State#state{events = [Event | Events]};
State#state{events = [Event | Events]};
_ ->
State
State
end;
%% Ignore all other events. We only care about messages and spawns/exits.
handleEvent(_, State) ->
State.
maybeKeepEvent(Event, From0, To0, State = #state{events = Events, pids = Pids}) ->
From = hide_pid_node(From0),
To = hide_pid_node(To0),
From = hidePidNode(From0),
To = hidePidNode(To0),
case {lists:member(From, Pids), lists:member(To, Pids)} of
{true, true} -> State#state{events = [Event | Events]};
_ -> State
end.
preparePids(Pids) ->
[hide_pid_node(Pid) || Pid <- Pids].
Pids.
%% [hide_pid_node(Pid) || Pid <- Pids].
hide_pid_node(Pid) when is_pid(Pid) -> hide_pid_node(pid_to_list(Pid));
hide_pid_node([$<, _, $. | Tail]) -> "<***." ++ Tail;
hide_pid_node([$<, _, _, $. | Tail]) -> "<***." ++ Tail;
hide_pid_node([$<, _, _, _, $. | Tail]) -> "<***." ++ Tail;
hide_pid_node([$<, _, _, _, _, $. | Tail]) -> "<***." ++ Tail;
hide_pid_node([$<, _, _, _, _, _, $. | Tail]) -> "<***." ++ Tail;
hide_pid_node(Name) -> Name.
hidePidNode(Pid) when is_pid(Pid) ->
Pid;
%%hide_pid_node(pid_to_list(Pid));
hidePidNode([$<, _, $. | Tail]) -> "<***." ++ Tail;
hidePidNode([$<, _, _, $. | Tail]) -> "<***." ++ Tail;
hidePidNode([$<, _, _, _, $. | Tail]) -> "<***." ++ Tail;
hidePidNode([$<, _, _, _, _, $. | Tail]) -> "<***." ++ Tail;
hidePidNode([$<, _, _, _, _, _, $. | Tail]) -> "<***." ++ Tail;
hidePidNode(Name) -> Name.
flush(State = #state{events = Events0}) ->
flush(#state{events = Events} = State) ->
%% Sort by timestamp from oldest to newest.
Events = lists:keysort(3, Events0),
SortEvents = lists:keysort(3, Events),
%% Initialize the formatting state.
put(num_calls, 0),
%% Output everything.
ok = file:write_file("seq.diag", [
"seqdiag {\n"
" edge_length = 300;\n"
" activation = none;\n"
"\n",
[format_event(Event, State) || Event <- Events],
"}\n"
]),
HeaderBin = <<"seqdiag {\n"
" edge_length = 300;\n"
" activation = none;\n"
"\n">>,
writeEvents(SortEvents, State, HeaderBin),
io:format(
"The file seq.diag was created. Use seqdiag to make a PNG.~n"
"$ seqdiag -Tpng --no-transparency seq.diag~n"
@ -98,42 +102,43 @@ flush(State = #state{events = Events0}) ->
"One line in the file is equal to a message sent by a process to another.~n"),
ok.
format_event({spawn, From, _, To, MFA}, State) ->
io_lib:format(" \"~w~s\" ->> \"~w~s\" [label=\"spawn ~9999P\"];~n", [
From, label(From, State), To, label(To, State), MFA, 8]);
format_event({exit, Pid, _, Reason}, State) ->
writeEvents([], _State, BinAcc) ->
LastBinAcc = <<BinAcc/binary, "}\n">>,
ok = file:write_file(<<"seq.diag">>, LastBinAcc);
writeEvents([Event | SortEvents], State, BinAcc) ->
EventBin = formatEvent(Event, State),
writeEvents(SortEvents, State, <<BinAcc/binary, EventBin/binary>>).
formatEvent({spawn, From, _, To, MFA}, State) ->
eFmt:formatBin(<<" \"~w~s\" ->> \"~w~s\" [label=\"spawn ~9999P\"];~n">>, [From, label(From, State), To, label(To, State), MFA, 8]);
formatEvent({exit, Pid, _, Reason}, State) ->
PidLabel = label(Pid, State),
io_lib:format(" \"~w~s\" ->> \"~w~s\" [label=\"exit ~9999P\"];~n", [
Pid, PidLabel, Pid, PidLabel, Reason, 8]);
format_event({Type, From, _, {'$gen_call', {From, Ref}, Msg}, To}, State) ->
eFmt:formatBin(<<" \"~w~s\" ->> \"~w~s\" [label=\"exit ~9999P\"];~n">>, [Pid, PidLabel, Pid, PidLabel, Reason, 8]);
formatEvent({Type, From, _, {'$gen_call', {From, Ref}, Msg}, To}, State) ->
NumCalls = get(num_calls) + 1,
put(num_calls, NumCalls),
put(Ref, NumCalls),
io_lib:format(" \"~w~s\" ~s \"~w~s\" [label=\"gen:call #~w ~9999P\"];~n", [
From, label(From, State),
case Type of send -> "->"; _ -> "-->" end,
To, label(To, State), NumCalls, Msg, 8]);
format_event(Event = {Type, From, _, {Ref, Msg}, To}, State) ->
eFmt:formatBin(<<" \"~w~s\" ~s \"~w~s\" [label=\"gen:call #~w ~9999P\"];~n">>, [From, label(From, State), case Type of send ->
"->"; _ -> "-->" end, To, label(To, State), NumCalls, Msg, 8]);
formatEvent(Event = {Type, From, _, {Ref, Msg}, To}, State) ->
case get(Ref) of
undefined ->
default_format_event(Event, State);
defFormatEvent(Event, State);
NumCall ->
io_lib:format(" \"~w~s\" ~s \"~w~s\" [label=\"#~w ~9999P\"];~n", [
From, label(From, State),
case Type of send -> "->"; _ -> "-->" end,
To, label(To, State), NumCall, Msg, 8])
eFmt:formatBin(<<" \"~w~s\" ~s \"~w~s\" [label=\"#~w ~9999P\"];~n">>, [From, label(From, State), case Type of send ->
"->"; _ -> "-->" end, To, label(To, State), NumCall, Msg, 8])
end;
format_event(Event, State) ->
default_format_event(Event, State).
formatEvent(Event, State) ->
defFormatEvent(Event, State).
default_format_event({Type, From, _, Msg, To}, State) ->
io_lib:format(" \"~w~s\" ~s \"~w~s\" [label=\"~9999P\"];~n", [
From, label(From, State),
case Type of send -> "->"; _ -> "-->" end,
To, label(To, State), Msg, 8]).
defFormatEvent({Type, From, _, Msg, To}, State) ->
eFmt:formatBin(<<" \"~w~s\" ~s \"~w~s\" [label=\"~9999P\"];~n">>, [From, label(From, State), case Type of send ->
"->"; _ -> "-->" end, To, label(To, State), Msg, 8]).
label(P, #state{meta = Meta}) ->
case maps:get(P, Meta, #{}) of
#{process_type := PT} -> io_lib:format(" (~w)", [PT]);
_ -> ""
#{process_type := PT} ->
eFmt:formatBin(" (~w)", [PT]);
_ ->
<<"">>
end.

+ 5
- 2
src/tracer/tpTracerConsole.erl Zobrazit soubor

@ -1,8 +1,11 @@
-module(tpTracerConsole).
-export([start_link/2]).
-export([init/1]).
-export([loop/1]).
-export([
init/1
, loop/1
]).
-export([system_continue/3]).
-export([system_terminate/4]).

Načítá se…
Zrušit
Uložit