• [Erlang21]Erlang性能分析工具eprof fporf的应用


    前段时间项目改代码突然cpu波动很大,排查了好久都没有找到原因,只能求助于性能测试工具
     
    <<Erlang程序设计>>----Joe Armstorng[哈哈,登月第一人也叫Armstrong]
    P416
    cprof测试每个函数被调用了多少次,这个工具为轻量在运行系统上使用这个工具会给系统带来5%~10%的额外负载
    fprof显示函数调用和被调用的埋单,并将结果输出到一个文件中,这个工具比较适合于在实验环境或模拟环境中对进行大规模的性能前一段,它会带来非常显著的系统负载.
    eprof分分析一个Erlang程序中计算时间主要消耗在哪里,它实际是fprof的前任,不同在于:这适合规模小一些的性能前评估.
     
    于是自己写了封装了一下,便于使用:
    你可以使用seek_proc_info:help()查看到具体函数的作用:可以把这个当做cprof和fprof使用例子吧。
     
    2> seek_proc_info:help().
    Brief help:
    seek_proc_info:queue(N) - show top N pids sorted by queue length
    seek_proc_info:memory(N) - show top N pids sorted by memory usage
    seek_proc_info:reds(N) - show top N pids sorted by reductions
    Erlang shell with Ctrl+C
    seek_proc_info:eprof_start() - start eprof on all available pids; DO NOT use on production system!
    seek_proc_info:eprof_stop() - stop eprof and print result
    seek_proc_info:fprof_start() - start fprof on all available pids; DO NOT use on production system!
    seek_proc_info:fprof_stop() - stop eprof and print formatted result
    seek_proc_info:fprof_start(N) - start and run fprof for N seconds; use seek_proc_info:fprof_analyze() to analyze collected statistics and print formatted result; use on production system with CARE
    seek_proc_info:fprof_analyze() - analyze previously collected statistics using seek_proc_info:fprof_start(N) and print formatted result
    seek_proc_info:help() - print this help
    ok
    只把你想监控的Application加到宏APPS里面,可以查看进程消息,内存,reduction情况。
    源代码在下面,也可以自己改改看进程的其它信息哦。
    %%%-------------------------------------------------------------------
    %%% @author zhongwencool@gmail.com
    %%% @doc
    %%%   A interface for eprof and fprof
    %%%   call ?MODULE:help() to see
    %%% @end
    %%% Created : 03. Sep 2014 3:09 PM
    %%%-------------------------------------------------------------------
    -module(seek_proc_info).
    
    %% API
    -export([eprof_start/0, eprof_stop/0,
      fprof_start/0, fprof_start/1,
      fprof_stop/0, fprof_analyze/0,
      queue/1, memory/1,
      reds/1, help/0
    ]).
    
    -define(APPS, [kernel,mnesia]).
    
    %%====================================================================
    %% API
    %%====================================================================
    help() ->
      io:format("Brief help:~n"
      "~p:queue(N) - show top N pids sorted by queue length~n"
      "~p:memory(N) - show top N pids sorted by memory usage~n"
      "~p:reds(N) - show top N pids sorted by reductions~n"
      "Erlang shell with Ctrl+C~n"
      "~p:eprof_start() - start eprof on all available pids; "
      "DO NOT use on production system!~n"
      "~p:eprof_stop() - stop eprof and print result~n"
      "~p:fprof_start() - start fprof on all available pids; "
      "DO NOT use on production system!~n"
      "~p:fprof_stop() - stop eprof and print formatted result~n"
      "~p:fprof_start(N) - start and run fprof for N seconds; "
      "use ~p:fprof_analyze() to analyze collected statistics and "
      "print formatted result; use on production system with CARE~n"
      "~p:fprof_analyze() - analyze previously collected statistics "
      "using ~p:fprof_start(N) and print formatted result~n"
      "~p:help() - print this help~n",
        lists:duplicate(12, ?MODULE)).
    
    eprof_start() ->
      eprof:start(),
      case lists:keyfind(running, 1, application:info()) of
        {_, Apps} ->
          case get_procs(?APPS, Apps) of
            [] ->
              {error, no_procs_found};
            Procs ->
              eprof:start_profiling(Procs)
          end;
        _ ->
          {error, no_app_info}
      end.
    
    fprof_start() ->
      fprof_start(0).
    
    fprof_start(Duration) ->
      case lists:keyfind(running, 1, application:info()) of
        {_, Apps} ->
          case get_procs(?APPS, Apps) of
            [] ->
              {error, no_procs_found};
            Procs ->
              fprof:trace([start, {procs, Procs}]),
              io:format("Profiling started~n"),
              if Duration > 0 ->
                timer:sleep(Duration*1000),
                fprof:trace([stop]),
                fprof:stop();
                true->
                  ok
              end
          end;
        _ ->
          {error, no_app_info}
      end.
    
    fprof_stop() ->
      fprof:trace([stop]),
      fprof:profile(),
      fprof:analyse([totals, no_details, {sort, own},
        no_callers, {dest, "fprof.analysis"}]),
      fprof:stop(),
      format_fprof_analyze().
    
    fprof_analyze() ->
      fprof_stop().
    
    eprof_stop() ->
      eprof:stop_profiling(),
      eprof:analyze().
    
    queue(N) ->
      dump(N, lists:reverse(lists:ukeysort(1, all_pids(queue)))).
    
    memory(N) ->
      dump(N, lists:reverse(lists:ukeysort(3, all_pids(memory)))).
    
    reds(N) ->
      dump(N, lists:reverse(lists:ukeysort(4, all_pids(reductions)))).
    
    %%====================================================================
    %% Internal functions
    %%====================================================================
    get_procs(Apps, AppList) ->
      io:format("Searching for processes to profile...~n", []),
      Procs = lists:foldl(
        fun({App, Leader},Acc) when is_pid(Leader) ->
          case lists:member(App, Apps) of
            true ->
              [get_procs2(Leader)|Acc];
            false ->
              Acc
          end;
          (_,Acc) ->
            Acc
        end,[], AppList),
      io:format("Found ~p processes~n", [length(Procs)]),
      Procs.
    
    get_procs2(Leader) ->
      lists:filter(
        fun(Pid) ->
          case process_info(Pid, group_leader) of
            {_, Leader} ->
              true;
            _ ->
              false
          end
        end, processes()).
    
    format_fprof_analyze() ->
      case file:consult("fprof.analysis") of
        {ok, [_, [{totals, _, _, TotalOWN}] | Rest]} ->
          OWNs =
            lists:flatmap(
            fun({MFA, _, _, OWN}) ->
              Percent = OWN*100/TotalOWN,
              case round(Percent) of
                0 -> [];
                _ -> [{mfa_to_list(MFA), Percent}]
              end
            end, Rest),
          ACCs = collect_accs(Rest),
          MaxACC = find_max(ACCs),
          MaxOWN = find_max(OWNs),
          io:format("=== Sorted by OWN:~n"),
          lists:foreach(
            fun({MFA, Per}) ->
              L = length(MFA),
              S = lists:duplicate(MaxOWN - L + 2, $ ),
              io:format("~s~s~.2f%~n", [MFA, S, Per])
            end, lists:reverse(lists:keysort(2, OWNs))),
          io:format("~n=== Sorted by ACC:~n"),
          lists:foreach(
            fun({MFA, Per}) ->
              L = length(MFA),
              S = lists:duplicate(MaxACC - L + 2, $ ),
              io:format("~s~s~.2f%~n", [MFA, S, Per])
            end, lists:reverse(lists:keysort(2, ACCs)));
        Err ->
          Err
      end.
    
    mfa_to_list({M, F, A}) ->
      atom_to_list(M) ++ ":" ++ atom_to_list(F) ++ "/" ++ integer_to_list(A);
    mfa_to_list(F) when is_atom(F) ->
      atom_to_list(F).
    
    find_max(List) ->
      find_max(List, 0).
    
    find_max([{V, _}|Tail], Acc) ->
      find_max(Tail, lists:max([length(V), Acc]));
    find_max([], Acc) ->
      Acc.
    
    collect_accs(List) ->
      List1 = lists:filter(
        fun({{sys, _, _}, _, _, _}) ->
          false;
          ({suspend,_,_,_}) ->
            false;
          ({{gen_fsm, _, _},_,_,_}) ->
            false;
          ({{gen, _, _},_,_,_}) ->
            false;
          ({{gen_server, _, _},_,_,_}) ->
            false;
          ({{proc_lib, _, _},_,_,_}) ->
            false;
          (_) ->
            true
        end, List),
      calculate(List1).
    
    calculate(List1) ->
      TotalACC = lists:sum([A || {_, _, A, _} <- List1]),
      List2 = lists:foldl(fun({MFA, _, ACC, _},NewList) ->
        Percent = ACC*100/TotalACC,
        case round(Percent) of
          0 -> NewList;
          _ -> [{mfa_to_list(MFA), Percent}|NewList]
         end
       end,[],List1),
      lists:reverse(List2).
    
    all_pids(Type) ->
      lists:foldl(
        fun(P, Acc) when P == self() ->
          Acc;
          (P, Acc) ->
            case catch process_info(
              P,[message_queue_len, memory, reductions,
                dictionary, current_function, registered_name]) of
              [{_, Len}, {_, Memory}, {_, Reds},
                {_, Dict}, {_, CurFun}, {_, RegName}] ->
                IntQLen = get_internal_queue_len(Dict),
                if Type == queue andalso Len == 0 andalso IntQLen == 0 ->
                  Acc;
                  true ->
                    [{lists:max([Len, IntQLen]),
                      Len,Memory, Reds, Dict, CurFun, P, RegName}|Acc]
                end;
              _ ->
                Acc
            end
        end, [], processes()).
    
    get_internal_queue_len(Dict) ->
      case lists:keysearch('$internal_queue_len', 1, Dict) of
        {value, {_, N}} -> N;
        _ -> 0
      end.
    
    dump(N, Rs) ->
      lists:foreach(
        fun({_, MsgQLen, Memory, Reds, Dict, CurFun, Pid, RegName}) ->
          io:format("** pid(~s)~n"
          "** registered name: ~p~n"
          "** memory: ~p~n"
          "** reductions: ~p~n"
          "** message queue len: ~p~n"
          "** current_function: ~p~n"
          "** dictionary: ~p~n~n",
            [pid_to_list(Pid), RegName, Memory, Reds, MsgQLen, CurFun, Dict])
        end, lists:sublist(Rs,N)).
    View Code

     

     洋葱、萝卜和西红柿,不相信世界上有南瓜这种东西。它们认为那是一种空想。南瓜不说话,只是默默地成长。——于尔克·舒比格《当世界年纪还小的时候》
  • 相关阅读:
    JS 中 原生方法 (四) --- Object
    在 JavaScript 中 prototype 和 __proto__ 有什么区别
    VueJs 源码分析 ---(一) 整体对 vuejs 框架的理解
    Node.js API 初解读(三)
    npm 包的 发布 流程
    JS 中 原生方法 (三) --- Date 日期
    JS 中 原生方法 (二) --- 数组 (修---添加ES6新增)
    Mac 下VIM配置
    css预处理器(sass)
    BFC 神奇背后的原理
  • 原文地址:https://www.cnblogs.com/zhongwencool/p/erlang_cprof_fprof.html
Copyright © 2020-2023  润新知