Skip to content
This repository has been archived by the owner on Sep 22, 2024. It is now read-only.

Commit

Permalink
Merge branch 'master' into ets-node-view-tab
Browse files Browse the repository at this point in the history
  • Loading branch information
mkacper committed Jul 26, 2017
2 parents 6c9964a + 67d57ff commit d6e3f42
Show file tree
Hide file tree
Showing 34 changed files with 1,516 additions and 659 deletions.
14 changes: 14 additions & 0 deletions apps/epl/src/epl_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ start(_StartType, _StartArgs) ->

ets:insert(epl_priv, {node_settings, NodeSettings}),

%% Store timestamp on when epl instance was started
%% Used for front-end caching
ets:insert(epl_priv, {started_at, get_timestamp()}),

%% Load priv files to ets
ok = run4(),

Expand All @@ -66,6 +70,9 @@ start(_StartType, _StartArgs) ->
%% Start EPL Dashboard
{ok, _} = epl_sup:start_child(epl_traffic, [], worker),

%% Start EPL Timeline
{ok, _} = epl_sup:start_child(epl_timeline, [], worker),

%% load plugins
PluginApps = plugins(Args),

Expand Down Expand Up @@ -454,3 +461,10 @@ maybe_start_elixir(Args) ->
_ ->
?DEBUG("Couldn't start Elixir~n", [])
end.


%% Retuns current timestamp in milliseconds
-spec get_timestamp() -> integer().
get_timestamp() ->
{Mega, Sec, Micro} = os:timestamp(),
(Mega*1000000 + Sec)*1000 + round(Micro/1000).
4 changes: 3 additions & 1 deletion apps/epl/src/epl_dashboard_EPL.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
init({tcp, http}, _Req, _Opts) ->
[{node, Node}] = epl:lookup(node),
[{node_settings, NodeSettings}] = epl:lookup(node_settings),
[{started_at, Timestamp}] = epl:lookup(started_at),
FormattedSettings = lists:foldl(fun format_node_settings/2,
#{node_name => Node}, NodeSettings),
#{node_name => Node,
started_at => Timestamp}, NodeSettings),
JSON = epl_json:encode(FormattedSettings, <<"system-init">>),
self() ! {data, JSON},
epl_dashboard:subscribe(),
Expand Down
129 changes: 129 additions & 0 deletions apps/epl/src/epl_timeline.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
%%% Copyright (c) 2017, erlang.pl
%%%-------------------------------------------------------------------
%%% @doc
%%% Tracking all timeline observers
%%% @end
%%%-------------------------------------------------------------------
-module(epl_timeline).
-behaviour(gen_server).

%% API
-export([start_link/0,
subscribe/0,
unsubscribe/0,
handle_pid/2]).

%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).

-record(state, {subscribers = [],
timelines = []}).

%%%===================================================================
%%% API functions
%%%===================================================================

start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

subscribe() ->
gen_server:cast(?MODULE, {subscribe, self()}).

unsubscribe() ->
gen_server:cast(?MODULE, {unsubscribe, self()}).

handle_pid(Action, P) when is_list(P) ->
try list_to_pid(P) of
Pid -> gen_server:cast(?MODULE, {Action, Pid})
catch
error:badarg -> noop
end;

handle_pid(Action, Pid) when is_binary(Pid) ->
handle_pid(Action, binary_to_list(Pid)).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

init([]) ->
ok = epl:subscribe(default_node),
{ok, #state{}}.

handle_call(_Request, _From, State) ->
{reply, ok, State}.

handle_cast({subscribe, Pid}, State = #state{subscribers = Subs}) ->
{noreply, State#state{subscribers = [Pid|Subs]}};
handle_cast({unsubscribe, Pid}, State = #state{subscribers = Subs}) ->
NextSubs = lists:delete(Pid, Subs),
{noreply, State#state{subscribers = NextSubs}};
handle_cast({add, Pid}, State = #state{timelines = Timelines}) when is_pid(Pid) ->
T = case lists:any(fun({P,_,_}) -> P == Pid end, Timelines) of
true -> Timelines;
_ -> [track_timeline(Pid)|Timelines]
end,
{noreply, State#state{timelines = T}};
handle_cast({remove, Pid}, State = #state{timelines = Timelines}) when is_pid(Pid) ->
NextTimelines = lists:filter(fun({P,_,_}) -> P /= Pid end, Timelines),
{noreply, State#state{timelines = NextTimelines}};
handle_cast(_Msg, State) ->
{noreply, State}.

handle_info({data, _, Data}, State = #state{subscribers = Subs, timelines = Timelines}) ->
{Maped, Updated} = lists:foldr(fun (T, A) -> update_timeline(T, A, Data) end, {[], []}, Timelines),
JSON = epl_json:encode(#{timelines => Maped}, <<"timeline-info">>),
[Pid ! {data, JSON} || Pid <- Subs],
{noreply, State#state{timelines = Updated}}.

terminate(_Reason, _State) ->
ok.

code_change(_OldVsn, State, _Extra) ->
{ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================

update_timeline({Pid, PidList, Changes}, {Maped, Updated}, Data) ->
Merged = merge_changes(Data, PidList, Changes),
Map = #{pid => epl:to_bin(Pid),
timeline => lists:map(fun({M, S}) ->
#{message => to_string(M),
state => to_string(S)}
end, Merged)},
{[Map|Maped], [{Pid, PidList, Merged}|Updated]}.

track_timeline(Pid) ->
epl_tracer:track_timeline(Pid),
{Pid, pid_to_list(Pid), []}.

to_string(Data) ->
S = io_lib:format("~p",[Data]),
epl:to_bin(lists:flatten(S)).

merge_changes(Data, Pid, Changes) ->
parse_data(Data, Pid) ++ Changes.

parse_data(Data, Pid) ->
ListOfProcessHistories = strip_to_timeline(Data),
strip_to_raw_data(ListOfProcessHistories, Pid).

strip_to_timeline(Data) ->
case lists:keyfind(timeline, 1, Data) of
false -> [];
[] -> [];
{timeline, Timelines} -> Timelines
end.

strip_to_raw_data(Data, Pid) ->
case lists:keyfind(Pid, 1, Data) of
false -> [];
{_,RawData} -> RawData
end.
51 changes: 51 additions & 0 deletions apps/epl/src/epl_timeline_EPL.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
%%% Copyright (c) 2017, erlang.pl
%%%-------------------------------------------------------------------
%%% @doc
%%% WebSocket handler returning info from timeline observers
%%% @end
%%%-------------------------------------------------------------------
-module(epl_timeline_EPL).
-behaviour(cowboy_websocket_handler).

%% cowboy_websocket_handler callbacks
-export([init/3,
websocket_init/3,
websocket_handle/3,
websocket_info/3,
websocket_terminate/3]).

%%%===================================================================
%%% cowboy_websocket_handler callbacks
%%%===================================================================
init({tcp, http}, _Req, _Opts) ->
Node = epl:get_default_node(),
{ok, AppsInfo} = epl_tracer:command(Node, fun application:info/0, []),
{_, Running} = lists:keyfind(running, 1, AppsInfo),
{_, Pid} = lists:keyfind(kernel, 1, Running),
JSON = epl_json:encode(#{pid => Pid}, <<"timeline-init">>),
self() ! {data, JSON},
epl_timeline:subscribe(),
{upgrade, protocol, cowboy_websocket}.

websocket_init(_TransportName, Req, _Opts) ->
{ok, Req, undefined_state}.

websocket_handle({text, Data}, Req, State) ->
case epl_json:decode(Data) of
[<<"add">>, Pid] -> epl_timeline:handle_pid(add, Pid);
[<<"remove">>, Pid] -> epl_timeline:handle_pid(remove, Pid);
_ -> invalid_msg
end,
{ok, Req, State};

websocket_handle(Data, _Req, _State) ->
exit({not_implemented, Data}).

websocket_info({data, Data}, Req, State) ->
{reply, {text, Data}, Req, State};
websocket_info(Info, _Req, _State) ->
exit({not_implemented, Info}).

websocket_terminate(_Reason, _Req, _State) ->
epl_timeline:unsubscribe(),
ok.
48 changes: 42 additions & 6 deletions apps/epl/src/epl_tracer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
unsubscribe/2,
command/3,
trace_pid/1,
track_timeline/1,
enable_ets_call_tracing/1,
disable_ets_call_tracing/1]).

Expand Down Expand Up @@ -75,6 +76,10 @@ enable_ets_call_tracing(Node) ->
disable_ets_call_tracing(Node) ->
gen_server:call(Node, disable_ets_call_tracing).

track_timeline(Pid) ->
Node = erlang:node(Pid),
gen_server:cast(Node, {track_timeline, Pid}).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
Expand All @@ -83,13 +88,16 @@ init(Node) ->
"fun (F, Ref, init) ->
%% create neccessary tables
EtsOptions = [named_table, ordered_set, private],
%% timeline needs two tables, one for caching messages, second for storing tracked pids
epl_timeline_cache = ets:new(epl_timeline_cache, [named_table, set, private]),
epl_timeline_pids = ets:new(epl_timeline_pids, [named_table, set, private]),
epl_receive = ets:new(epl_receive, EtsOptions),
epl_send = ets:new(epl_send, EtsOptions),
epl_send_self = ets:new(epl_send_self, EtsOptions),
epl_spawn = ets:new(epl_spawn, EtsOptions),
epl_exit = ets:new(epl_exit, EtsOptions),
epl_trace = ets:new(epl_trace, [named_table,bag,private]),
epl_ets_func = ets:new(epl_ets_func,
epl_ets_func = ets:new(epl_ets_func,
[named_table, duplicate_bag, private]),
%% turn on tracer for all processes
Expand All @@ -98,17 +106,33 @@ init(Node) ->
F(F, Ref, undefined);
(F, Ref, Trace) ->
receive
{trace_ts, Pid, 'receive', Msg, _TS} ->
{trace_ts, Pid, 'receive', Msg, _TS} when Pid /= self() ->
%% we count received messages and their sizes
Size = erts_debug:flat_size(Msg),
case ets:lookup(epl_timeline_pids, Pid) of
[] -> ok;
_ ->
TimelineOld = case ets:lookup(epl_timeline_cache, Pid) of
[] -> [];
[{Pid, Timeline}] -> Timeline
end,
case Msg of
{system, _,_} -> ok;
_ ->
NewValue = {Msg, sys:get_state(Pid)},
ets:delete(epl_timeline_cache, Pid),
ets:insert(epl_timeline_cache, {Pid, [NewValue | TimelineOld]})
end
end,
case ets:lookup(epl_receive, Pid) of
[] -> ets:insert(epl_receive, {Pid, 1, Size});
_ -> ets:update_counter(epl_receive,
Pid, [{2, 1}, {3, Size}])
end,
F(F, Ref, Trace);
{trace_ts, Pid1, send, _Msg, Pid2, _TS}
when Pid1 < Pid2 ->
when Pid1 < Pid2, Pid1 /= self(), Pid2 /= self() ->
%% we have one key for each Pid pair
%% the smaller Pid is first element of the key
case ets:lookup(epl_send, {Pid1, Pid2}) of
Expand All @@ -118,7 +142,7 @@ init(Node) ->
end,
F(F, Ref, Trace);
{trace_ts, Pid1, send, _Msg, Pid2, _TS}
when Pid1 > Pid2 ->
when Pid1 > Pid2, Pid1 /= self(), Pid2 /= self() ->
%% we have one key for each Pid pair
%% the smaller Pid is first element of the key
case ets:lookup(epl_send, {Pid2, Pid1}) of
Expand Down Expand Up @@ -195,6 +219,9 @@ init(Node) ->
|| {Key, Fun, Args} <- List],
Pid ! {Ref, Proplist},
F(F, Ref, Trace);
{Ref, Pid, {track_timeline, Tracked}} ->
ets:insert(epl_timeline_pids, {Tracked, []}),
F(F, Ref, Trace);
M ->
%% if we receive an unknown message
%% we stop tracing and exit
Expand All @@ -210,6 +237,9 @@ init(Node) ->

{ok, #state{ref = Ref, remote_pid = RemotePid}, ?POLL}.

handle_cast({track_timeline, Pid}, State = #state{remote_pid = RPid, ref = Ref}) ->
RPid ! {Ref, self(), {track_timeline, Pid}},
{noreply, State, ?POLL};
handle_cast(Request, _State) ->
exit({not_implemented, Request}).

Expand Down Expand Up @@ -251,6 +281,8 @@ handle_info(timeout,
{memory_total, fun erlang:memory/1, [total]},
{spawn, fun ets:tab2list/1, [epl_spawn]},
{spawn_, fun ets:delete_all_objects/1, [epl_spawn]},
{timeline, fun ets:tab2list/1, [epl_timeline_cache]},
{timeline_, fun ets:delete_all_objects/1, [epl_timeline_cache]},
{exit, fun ets:tab2list/1, [epl_exit]},
{exit_, fun ets:delete_all_objects/1, [epl_exit]},
{send, fun ets:tab2list/1, [epl_send]},
Expand All @@ -271,7 +303,7 @@ handle_info(timeout,
{Ref, Proplist} when is_list(Proplist) ->
%% assert all ets tables were cleared
EtsTables = [spawn_, exit_, send_, send_self_, receive_, trace_,
ets_func_],
timeline_, ets_func_],
[{Key, true} = lists:keyfind(Key, 1, Proplist)
|| Key <- EtsTables],

Expand All @@ -282,6 +314,10 @@ handle_info(timeout,
Proplist,
EtsTables),

{value, {timeline, Timelines}, PropsListWithoutFaultyTimelines } = lists:keytake(timeline, 1, Proplist1),
PidsAsLists = lists:map(fun({Key, Value}) -> {pid_to_list(Key), Value} end, Timelines),
Proplist2 = [{timeline, PidsAsLists} | PropsListWithoutFaultyTimelines],


%% [{process_count,34},
%% {memory_total,5969496},
Expand All @@ -303,7 +339,7 @@ handle_info(timeout,
%% {<5984.30.0>,2,24}]}]

Key = {node(RPid), os:timestamp()},
[Pid ! {data, Key, Proplist1} || Pid <- Subs],
[Pid ! {data, Key, Proplist2} || Pid <- Subs],

{noreply, State, ?POLL}
after 5000 ->
Expand Down
2 changes: 1 addition & 1 deletion apps/epl_ets/src/epl_ets.app.src
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
[{description, "EPL plugin for visualising ETS characteristics"},
{vsn, "0.1.0"},
{registered, []},
{mod, { epl_ets_app, []}},
{mod, {epl_ets_app, []}},
{applications,
[kernel,
stdlib,
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@
"copy-webpack-plugin": "^4.0.1",
"font-awesome": "^4.7.0",
"humps": "^2.0.0",
"immutable": "^3.8.1",
"lodash": "^4.17.4",
"react": "^15.4.2",
"react-bootstrap": "^0.30.7",
"react-dom": "^15.4.2",
"react-elm-components": "^1.0.1",
"react-highlight": "^0.9.0",
"react-measure": "^1.4.5",
"react-motion": "^0.4.7",
"react-redux": "^5.0.2",
Expand Down
Loading

0 comments on commit d6e3f42

Please sign in to comment.