• erlang 故障排查工具


     系统级别perf top, dstat -tam, vtune 都能很好分析beam 瓶颈,本文主要erlang 级别排查:

    1. 反编译

       确认线上运行代码是否正确,reltools没掌握好,升级偶尔出现问题

    decompile(Mod) ->
        {ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(code:which(Mod), [abstract_code]),
        io:format("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).

    2. 进程栈

        类似于jstack,发现大量进程挂起,进程数过高,运行慢,hang住等问题用到

    pstack(Reg) when is_atom(Reg) ->
        case whereis(Reg) of
            undefined -> undefined;
            Pid -> pstack(Pid)
        end;
    pstack(Pid) ->
        io:format("~s~n", [element(2, process_info(Pid, backtrace))]).

    3. etop

      分析内存、cpu占用进程,即使数十w进程node 也能正常使用

    %进程CPU占用排名
    etop() ->
        spawn(fun() -> etop:start([{output, text}, {interval, 10}, {lines, 20}, {sort, reductions}]) end).
    
    %进程Mem占用排名
    etop_mem() ->
        spawn(fun() -> etop:start([{output, text}, {interval, 10}, {lines, 20}, {sort, memory}]) end).
    
    %停止etop
    etop_stop() ->
        etop:stop().

    4. gc all

       进程内存过高时,来一发,看看是内存泄露还是gc不过来

    % 对所有process做gc
    gc_all() ->
        [erlang:garbage_collect(Pid) || Pid <- processes()].

    5.  fprof

    % 对MFA 执行分析,会严重减缓运行,建议只对小量业务执行
    % 结果:
    % fprof 结果比较详细,能够输出热点调用路径
    fprof(M, F, A) ->
        fprof:start(),
        fprof:apply(M, F, A),
        fprof:profile(),
        fprof:analyse(),
        fprof:stop().

    6. eprof

    % 对整个节点内所有进程执行eprof, eprof 对线上业务有一定影响,慎用!
    % 建议TimeoutSec<10s,且进程数< 1000,否则可能导致节点crash
    % 结果:
    % 输出每个方法实际执行时间(不会累计方法内其他mod调用执行时间)
    % 只能得到mod - Fun 执行次数 执行耗时
    eprof_all(TimeoutSec) ->
        eprof(processes() -- [whereis(eprof)], TimeoutSec).
    
    eprof(Pids, TimeoutSec) ->
        eprof:start(),
        eprof:start_profiling(Pids),
        timer:sleep(TimeoutSec),
        eprof:stop_profiling(),
        eprof:analyze(total),
        eprof:stop().

    7. scheduler usage

    % 统计下1s每个调度器CPU的实际利用率(因为有spin wait、调度工作, 可能usage 比top显示低很多)
    scheduler_usage() ->
        scheduler_usage(1000).
    
    scheduler_usage(RunMs) ->
        erlang:system_flag(scheduler_wall_time, true),
        Ts0 = lists:sort(erlang:statistics(scheduler_wall_time)),
        timer:sleep(RunMs),
        Ts1 = lists:sort(erlang:statistics(scheduler_wall_time)),
        erlang:system_flag(scheduler_wall_time, false),
        Cores = lists:map(fun({{I, A0, T0}, {I, A1, T1}}) ->
                        {I, (A1 - A0) / (T1 - T0)} end, lists:zip(Ts0, Ts1)),
        {A, T} = lists:foldl(fun({{_, A0, T0}, {_, A1, T1}}, {Ai,Ti}) ->
                        {Ai + (A1 - A0), Ti + (T1 - T0)} end, {0, 0}, lists:zip(Ts0, Ts1)),
        Total = A/T,
        io:format("~p~n", [[{total, Total} | Cores]]).

    8.  进程调度

    % 统计下1s内调度进程数量(含义:第一个数字执行进程数量,第二个数字迁移进程数量)
    scheduler_stat() ->
        scheduler_stat(1000).
    
    scheduler_stat(RunMs) ->
        erlang:system_flag(scheduling_statistics, enable),
        Ts0 = erlang:system_info(total_scheduling_statistics),
        timer:sleep(RunMs),
        Ts1 = erlang:system_info(total_scheduling_statistics),
        erlang:system_flag(scheduling_statistics, disable),
        lists:map(fun({{Key, In0, Out0}, {Key, In1, Out1}}) ->
                    {Key, In1 - In0, Out1 - Out0} end, lists:zip(Ts0, Ts1)).

    9. trace 日志

      会把mod 每次调用详细MFA log 下来,args 太大就不好看了

    %trace Mod 所有方法的调用
    trace(Mod) ->
        dbg:tracer(),
        dbg:tpl(Mod, '_', []),
        dbg:p(all, c).
    
    %trace Node上指定 Mod 所有方法的调用, 结果将输出到本地shell
    trace(Node, Mod) ->
        dbg:tracer(),
        dbg:n(Node),
        dbg:tpl(Mod, '_', []),
        dbg:p(all, c).
    
    %停止trace
    trace_stop() ->
        dbg:stop_clear().

    10. 内存高OOM 排查工具

      etop 无法应对10w+ 进程节点, 下面代码就没问题了;找到可疑proc后通过pstack、message_queu_len 排查原因

      proc_mem_all(SizeLimitKb) ->
          Procs = [{undefined, Pid} || Pid<- erlang:processes()],
          proc_mem(Procs, SizeLimitKb).
    
      proc_mem(SizeLimitKb) ->
          Procs = [{Name, Pid} || {_, Name, Pid, _} <- release_handler_1:get_supervised_procs(),
                                  is_process_alive(Pid)],
          proc_mem(Procs, SizeLimitKb).
    
      proc_mem(Procs, SizeLimitKb) ->
          SizeLimit = SizeLimitKb * 1024,
          {R, Total} = lists:foldl(fun({Name, Pid}, {Acc, TotalSize}) ->
              case erlang:process_info(Pid, total_heap_size) of
                  {_, Size0} ->
                      Size = Size0*8,
                      case Size > SizeLimit of
                          true -> {[{Name, Pid, Size} | Acc], TotalSize+Size};
                          false -> {Acc, TotalSize}
                      end;
                  _ -> {Acc, TotalSize}
                  end
              end, {[], 0}, Procs),
          R1 = lists:keysort(3, R),
          {Total, lists:reverse(R1)}.
  • 相关阅读:
    iOS 代码规范
    Clang Format
    PullToRefreshListView手动刷新问题
    android不依赖具体activity弹出Dialog对话框,即全局性对话框
    我的手机华为荣耀7,运行android程序不输出Log
    Android从imageview中获得bitmap
    Fragment里面的ViewPager嵌套subFragment,主Fragment切换的时候subFragment出现空白Fragment的Bug
    解决某些手机RadioGroup中的RadioButton不居中的问题
    Android权限大全
    UML类图
  • 原文地址:https://www.cnblogs.com/lulu/p/4149204.html
Copyright © 2020-2023  润新知