You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 regels
9.4 KiB

4 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
4 jaren geleden
3 jaren geleden
4 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
3 jaren geleden
  1. -module(tpFlame).
  2. -export([
  3. pfs/2 %% 分析单个文件
  4. , pfm/2 %% 分析多个文件
  5. ]).
  6. -record(state, {
  7. outputPath = "",
  8. pid,
  9. lastTs,
  10. count = 0,
  11. acc = []
  12. }).
  13. -spec pfs(file:filename_all(), file:filename_all()) -> ok.
  14. pfs(InputFile, OutputPath) ->
  15. {ok, FinalState} = tpFReader:fold(fun handleEvent/2, #state{outputPath = OutputPath}, InputFile),
  16. flush(FinalState).
  17. -spec pfm(file:filename(), file:filename()) -> ok.
  18. pfm(InputFiles, OutputPath) ->
  19. PfFiles = filelib:wildcard(InputFiles),
  20. doPfm(PfFiles, #state{outputPath = OutputPath}).
  21. doPfm([], State) ->
  22. flush(State);
  23. doPfm([InputFile | PfFiles], State) ->
  24. {ok, NewState} = tpFReader:fold(fun handleEvent/2, State, InputFile),
  25. doPfm(PfFiles, NewState).
  26. handleEvent({Type, Pid, Ts, Arg}, State) ->
  27. doExp({trace_ts, Pid, Type, Arg, Ts}, State);
  28. handleEvent({Type, Pid, Ts, Arg, ExtraOrMspec}, State) ->
  29. doExp({trace_ts, Pid, Type, Arg, ExtraOrMspec, Ts}, State);
  30. handleEvent({Type, Pid, Ts, Arg, Extra, Mspec}, State) ->
  31. doExp({trace_ts, Pid, Type, Arg, Extra, Mspec, Ts}, State).
  32. doExp(T, #state{outputPath = OutputPath} = State) ->
  33. trace_ts = element(1, T),
  34. Pid = element(2, T),
  35. PidState =
  36. case erlang:get(Pid) of
  37. undefined ->
  38. io:format("~p ", [Pid]),
  39. #state{outputPath = OutputPath};
  40. SomeState ->
  41. SomeState
  42. end,
  43. NewPidState = doExpInner(T, PidState),
  44. erlang:put(Pid, NewPidState),
  45. State.
  46. %% in & out, without call context, don't help us
  47. doExpInner({trace_ts, _Pid, InOut, _MFA, _TS}, #state{lastTs = undefined} = PS) when InOut == in; InOut == out ->
  48. PS;
  49. %% return_from and return_to, without call context, don't help us
  50. doExpInner({trace_ts, _Pid, Return, _MFA, _TS}, #state{lastTs = undefined} = PS) when Return == return_from; Return == return_to ->
  51. PS;
  52. doExpInner({trace_ts, Pid, call, MFA, BIN, TS}, #state{lastTs = LastTS, acc = Acc, count = Count} = PS) ->
  53. try
  54. %% Calculate time elapsed, TS-LastTs.
  55. %% 0. If Acc is empty, then skip step #1.
  56. %% 1. Credit elapsed time to the stack on the top of Acc.
  57. %% 2. Push a 0 usec item with this stack onto Acc.
  58. Stak =
  59. lists:filter(
  60. fun(<<"unknown function">>) -> false;
  61. (_) -> true
  62. end, stak_binify(BIN)),
  63. Stack0 = stak_trim(Stak),
  64. MFA_bin = mfa_binify(MFA),
  65. Stack1 = [MFA_bin | lists:reverse(Stack0)],
  66. Acc2 =
  67. case Acc of
  68. [] ->
  69. [{Stack1, 0}];
  70. [{LastStack, LastTime} | Tail] ->
  71. USec = TS - LastTS,
  72. % io:format("Stack1: ~p ~p\n", [Stack1, USec]),
  73. [{Stack1, 0},
  74. {LastStack, LastTime + USec} | Tail]
  75. end,
  76. %% TODO: more state tracking here.
  77. PS#state{pid = Pid, lastTs = TS, count = Count + 1, acc = Acc2}
  78. catch Class:Reason:StackTrace ->
  79. io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, Class, Reason, StackTrace]),
  80. PS
  81. end;
  82. doExpInner({trace_ts, _Pid, return_to, MFA, TS}, #state{lastTs = LastTS, acc = Acc} = S) ->
  83. try
  84. %% Calculate time elapsed, TS-LastTs.
  85. %% 1. Credit elapsed time to the stack on the top of Acc.
  86. %% 2. Push a 0 usec item with the "best" stack onto Acc.
  87. %% "best" = MFA exists in the middle of the stack onto Acc,
  88. %% or else MFA exists at the top of a stack elsewhere in Acc.
  89. [{LastStack, LastTime} | Tail] = Acc,
  90. MFA_bin = mfa_binify(MFA),
  91. BestStack = lists:dropwhile(fun(SomeMFA) when SomeMFA /= MFA_bin -> true;
  92. (_) -> false
  93. end, find_matching_stack(MFA_bin, Acc)),
  94. USec = TS - LastTS,
  95. Acc2 = [{BestStack, 0},
  96. {LastStack, LastTime + USec} | Tail],
  97. % io:format(user, "return-to: ~p\n", [lists:sublist(Acc2, 4)]),
  98. S#state{lastTs = TS, acc = Acc2}
  99. catch XX:YY:ZZ ->
  100. io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]),
  101. S
  102. end;
  103. doExpInner({trace_ts, _Pid, gc_start, _Info, TS}, #state{lastTs = LastTS, acc = Acc} = S) ->
  104. try
  105. %% Push a 0 usec item onto Acc.
  106. [{LastStack, LastTime} | Tail] = Acc,
  107. NewStack = [<<"GARBAGE-COLLECTION">> | LastStack],
  108. USec = TS - LastTS,
  109. Acc2 = [{NewStack, 0},
  110. {LastStack, LastTime + USec} | Tail],
  111. % io:format(user, "GC 1: ~p\n", [lists:sublist(Acc2, 4)]),
  112. S#state{lastTs = TS, acc = Acc2}
  113. catch _XX:_YY:_ZZ ->
  114. %% io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, _XX, _YY, _ZZ]),
  115. S
  116. end;
  117. doExpInner({trace_ts, _Pid, gc_end, _Info, TS}, #state{lastTs = LastTS, acc = Acc} = S) ->
  118. try
  119. %% Push the GC time onto Acc, then push 0 usec item from last exec
  120. %% stack onto Acc.
  121. [{GCStack, GCTime}, {LastExecStack, _} | Tail] = Acc,
  122. USec = TS - LastTS,
  123. Acc2 = [{LastExecStack, 0}, {GCStack, GCTime + USec} | Tail],
  124. % io:format(user, "GC 2: ~p\n", [lists:sublist(Acc2, 4)]),
  125. S#state{lastTs = TS, acc = Acc2}
  126. catch _XX:_YY:_ZZ ->
  127. %% io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, _XX, _YY, _ZZ]),
  128. S
  129. end;
  130. doExpInner({trace_ts, _Pid, out, MFA, TS}, #state{lastTs = LastTS, acc = Acc} = S) ->
  131. try
  132. %% Push a 0 usec item onto Acc.
  133. %% The MFA reported here probably doesn't appear in the stacktrace
  134. %% given to us by the last 'call', so add it here.
  135. [{LastStack, LastTime} | Tail] = Acc,
  136. MFA_bin = mfa_binify(MFA),
  137. NewStack = [<<"SLEEP">>, MFA_bin | LastStack],
  138. USec = TS - LastTS,
  139. Acc2 = [{NewStack, 0},
  140. {LastStack, LastTime + USec} | Tail],
  141. S#state{lastTs = TS, acc = Acc2}
  142. catch XX:YY:ZZ ->
  143. io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]),
  144. S
  145. end;
  146. doExpInner({trace_ts, _Pid, in, MFA, TS}, #state{lastTs = LastTS, acc = Acc} = S) ->
  147. try
  148. %% Push the Sleep time onto Acc, then push 0 usec item from last
  149. %% exec stack onto Acc.
  150. %% The MFA reported here probably doesn't appear in the stacktrace
  151. %% given to us by the last 'call', so add it here.
  152. MFA_bin = mfa_binify(MFA),
  153. [{SleepStack, SleepTime}, {LastExecStack, _} | Tail] = Acc,
  154. USec = TS - LastTS,
  155. Acc2 = [{[MFA_bin | LastExecStack], 0}, {SleepStack, SleepTime + USec} | Tail],
  156. S#state{lastTs = TS, acc = Acc2}
  157. catch XX:YY:ZZ ->
  158. io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]),
  159. S
  160. end;
  161. %exp1_inner(end_of_trace = _Else, #state{pid=Pid, output_path=OutputPath, acc=Acc} = S) ->
  162. % {ok, FH} = file:open(OutputPath, [write, raw, binary, delayed_write]),
  163. % io:format("Writing to ~s ... ", [OutputPath]),
  164. % [begin
  165. % Pid_str = io_lib:format("~w", [Pid]),
  166. % Time_str = integer_to_list(Time),
  167. % file:write(FH, [Pid_str, $;, intersperse($;, lists:reverse(Stack)), 32, Time_str, 10])
  168. % end || {Stack, Time} <- Acc],
  169. % file:close(FH),
  170. % io:format("finished\n"),
  171. % S;
  172. doExpInner(_Else, S) ->
  173. % io:format("?? ~P\n", [_Else, 10]),
  174. S.
  175. find_matching_stack(MFA_bin, [{H, _Time} | _] = Acc) ->
  176. case lists:member(MFA_bin, H) of
  177. true ->
  178. H;
  179. false ->
  180. find_matching_stack2(MFA_bin, Acc)
  181. end.
  182. find_matching_stack2(MFA_bin, [{[MFA_bin | _StackTail] = Stack, _Time} | _]) ->
  183. Stack;
  184. find_matching_stack2(MFA_bin, [_H | T]) ->
  185. find_matching_stack2(MFA_bin, T);
  186. find_matching_stack2(_MFA_bin, []) ->
  187. [<<"FIND-MATCHING-FAILED">>].
  188. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  189. intersperse(_, []) -> [];
  190. intersperse(_, [X]) -> [X];
  191. intersperse(Sep, [X | Xs]) -> [X, Sep | intersperse(Sep, Xs)].
  192. stak_trim([<<"proc_lib:init_p_do_apply/3">>, <<"gen_fsm:decode_msg/9">>, <<"gen_fsm:handle_msg/7">>, <<"gen_fsm:loop/7">> | T]) ->
  193. stak_trim([<<"GEN-FSM">> | T]);
  194. stak_trim([<<"GEN-FSM">>, <<"gen_fsm:decode_msg/9">>, <<"gen_fsm:handle_msg/7">>, <<"gen_fsm:loop/7">> | T]) ->
  195. stak_trim([<<"GEN-FSM">> | T]);
  196. stak_trim(Else) ->
  197. Else.
  198. stak_binify(Bin) when is_binary(Bin) ->
  199. [list_to_binary(X) || X <- stak(Bin)];
  200. stak_binify(X) ->
  201. list_to_binary(io_lib:format("~w", [X])).
  202. mfa_binify({M, F, A}) ->
  203. list_to_binary(io_lib:format("~w:~w/~w", [M, F, A]));
  204. mfa_binify(X) ->
  205. list_to_binary(io_lib:format("~w", [X])).
  206. %% Borrowed from redbug.erl
  207. stak(Bin) ->
  208. lists:foldl(fun munge/2, [], string:tokens(binary_to_list(Bin), "\n")).
  209. munge(I, Out) ->
  210. case I of %% lists:reverse(I) of
  211. "..." ++ _ -> ["truncated!!!" | Out];
  212. _ ->
  213. case string:str(I, "Return addr") of
  214. 0 ->
  215. case string:str(I, "cp = ") of
  216. 0 -> Out;
  217. _ -> [mfaf(I) | Out]
  218. end;
  219. _ ->
  220. case string:str(I, "erminate process normal") of
  221. 0 -> [mfaf(I) | Out];
  222. _ -> Out
  223. end
  224. end
  225. end.
  226. mfaf(I) ->
  227. [_, C | _] = string:tokens(I, "()+"),
  228. string:strip(C).
  229. flush(#state{outputPath = OutputPath}) ->
  230. PidStates = get(),
  231. {ok, FH} = file:open(OutputPath, [write, raw, binary, delayed_write]),
  232. io:format("\n\nWriting to ~s for ~w processes... ", [OutputPath, length(PidStates)]),
  233. _ = [
  234. [begin
  235. Pid_str0 = lists:flatten(io_lib:format("~w", [Pid])),
  236. Size = length(Pid_str0),
  237. Pid_str = [$(, lists:sublist(Pid_str0, 2, Size - 2), $)],
  238. Time_str = integer_to_list(Time),
  239. file:write(FH, [Pid_str, $;, intersperse($;, lists:reverse(Stack)), 32, Time_str, 10])
  240. end || {Stack, Time} <- Acc]
  241. || {Pid, #state{acc = Acc} = _S} <- PidStates],
  242. _ = file:close(FH),
  243. io:format("finished!\n"),
  244. ok.