Skip to content

Instantly share code, notes, and snippets.

@rlander
Forked from stolen/timetop.erl
Created February 27, 2018 18:19
Show Gist options
  • Save rlander/eff86a42fb018daea6281f93b3f055be to your computer and use it in GitHub Desktop.
Save rlander/eff86a42fb018daea6281f93b3f055be to your computer and use it in GitHub Desktop.

Revisions

  1. @stolen stolen created this gist Jan 18, 2018.
    94 changes: 94 additions & 0 deletions timetop.erl
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,94 @@
    -module(timetop).

    -export([top/2]).

    top(Duration, Count) ->
    OldPrio = erlang:process_flag(priority, high),
    Result = scheduled_time_top(Duration),
    erlang:process_flag(priority, OldPrio),
    lists:sublist(Result, Count).



    %% Build process top according to scheduled time
    %% For simplicity, we ignore newly started processes
    scheduled_time_top(Duration) when is_integer(Duration) ->
    % Start collecting trace data and remember when we did that
    erlang:trace(existing, true, [running,monotonic_timestamp]),
    StartTime = erlang:monotonic_time(nano_seconds),

    % Remember intermediate process states so we can guess if process was running all the time
    Pids = erlang:processes(),
    DefaultStates = [{P, S} || P <- Pids, P =/= self(), {status, S} <- [process_info(P, status)]],

    % Set trace finish timer
    TRef = erlang:start_timer(Duration, self(), stop_tracing),

    % Collect events until timer fires
    RevEvents0 = acc_sched_trace_events(StartTime, undefined, TRef, []),

    % Finish tracing, remember the time
    EndTime = erlang:monotonic_time(nano_seconds),
    erlang:trace(existing, false, [running,monotonic_timestamp]),

    % Collect remaining events
    FinTRef = erlang:start_timer(50, self(), stop_collecting),
    RevEvents1 = acc_sched_trace_events(StartTime, EndTime, FinTRef, []),

    % Concatenate all events and put them in order
    AllEvents = lists:reverse(RevEvents0, lists:reverse(RevEvents1)),

    % For every traced process, calculate its runnint time percentage
    RunPercentages = [{P, running_time_percentage(P, AllEvents, StartTime, EndTime, DefState)} || {P, DefState} <- DefaultStates, erlang:is_process_alive(P)],

    % Sort the result
    SortedRunPercentages = lists:sort(fun({_, P1}, {_, P2}) -> P1 >= P2 end, RunPercentages),

    % Trim percentages for short printing by default
    TrimmedSortedRunPercentages = [{P, round(R * 100) / 100} || {P, R} <- SortedRunPercentages],

    % Drop any extra trace messages
    drop_sched_trace_events(),

    TrimmedSortedRunPercentages.

    running_time_percentage(Pid, AllEvents, StartTime, EndTime, DefState) ->
    PidEvents = [{Timestamp, Ev} || {Timestamp, EvPid, Ev} <- AllEvents, EvPid == Pid, StartTime =< Timestamp, Timestamp =< EndTime],
    case PidEvents of
    [] when DefState == running ->
    100.0;
    [] ->
    0.0;
    Events ->
    caclculate_running_time(StartTime, EndTime, lists:sort(Events)) * 100.0 / (EndTime - StartTime)
    end.

    acc_sched_trace_events(StartTime, EndTime, TRef, Acc) ->
    Self = self(),
    receive
    {timeout, TRef, _} ->
    Acc;
    {trace_ts, Pid, Ev, _, Timestamp} when Pid /= Self, StartTime =< Timestamp, (EndTime == undefined orelse Timestamp =< EndTime) ->
    acc_sched_trace_events(StartTime, EndTime, TRef, [{Timestamp, Pid, Ev}|Acc]);
    {trace_ts, _, _, _, _} ->
    acc_sched_trace_events(StartTime, EndTime, TRef, Acc)
    end.

    drop_sched_trace_events() ->
    receive
    {trace_ts, _, _, _, _} -> drop_sched_trace_events()
    after 0 -> ok
    end.

    caclculate_running_time(_StartTime, _EndTime, []) ->
    0;

    caclculate_running_time(_StartTime, EndTime, [{SingleInTime, in}]) ->
    EndTime - SingleInTime;

    caclculate_running_time(StartTime, EndTime, [{OutTime, out} | Events]) ->
    % If we see a process out, assume it was initially in
    caclculate_running_time(StartTime, EndTime, [{StartTime, in}, {OutTime, out} | Events]);

    caclculate_running_time(_StartTime, EndTime, [{InTime, in}, {OutTime, out} | Events]) ->
    (OutTime - InTime) + caclculate_running_time(OutTime, EndTime, Events).