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

Commit

Permalink
Merge pull request #46 from erlanglab/merge-epl_st
Browse files Browse the repository at this point in the history
Merge epl_st as in-umbrella application
  • Loading branch information
arkgil authored Apr 13, 2017
2 parents 33c69e6 + d6ad0b2 commit 8c7f242
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 5 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ yarn-error.log*

# Erlang

apps/epl/.rebar
apps/epl/ebin
apps/**/.rebar
apps/**/ebin
apps/epl/priv/htdocs/
deps
erlangpl
Expand Down
2 changes: 0 additions & 2 deletions apps/epl/rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
{git, "git://github.com/jcomellas/getopt.git", "master"}},
{cowboy, ".*",
{git, "git://github.com/ninenines/cowboy.git", "0.8.5"}},
{epl_st, ".*",
{git, "git://github.com/erlanglab/epl_st.git", "0.1.0"}},
{jsone, ".*",
{git, "git://github.com/erlanglab/jsone.git", "master"}}
]
Expand Down
13 changes: 13 additions & 0 deletions apps/epl_st/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Copyright 2017 erlang.pl

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
4 changes: 4 additions & 0 deletions apps/epl_st/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
epl_st
=====

Erlang Performance Lab plugin for visualising applications' supervision trees.
2 changes: 2 additions & 0 deletions apps/epl_st/rebar.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{erl_opts, [debug_info]}.
{deps, []}.
19 changes: 19 additions & 0 deletions apps/epl_st/src/epl_st.app.src
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{application, epl_st,
[{description, "EPL plugin for visualising applications' supervision trees"},
{vsn, "0.1.1"},
{registered, []},
{mod, {epl_st_app, []}},
{applications,
[kernel,
stdlib,
epl
]},
{env,[]},
{modules, []},

{maintainers, ["Erlang Performance Lab"]},
{licenses, ["Apache 2.0"]},
{links,
[{"GitHub", "https://github.com/erlanglab/erlangpl"},
{"Erlang Performance Lab", "http://www.erlang.pl"}]}
]}.
174 changes: 174 additions & 0 deletions apps/epl_st/src/epl_st.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
%%%-------------------------------------------------------------------
%%% @doc
%%% gen_sever listening to events from epl_tracer and calculating
%%% supervision trees

%%% @end
%%%-------------------------------------------------------------------
-module(epl_st).

-behaviour(gen_server).

%% API
-export([start_link/0,
subscribe/0,
unsubscribe/0,
node_info/1]).

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

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

%%%===================================================================
%%% 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()}).

node_info(NodeId) when is_list(NodeId) ->
Info =
case node_id_to_pid(NodeId) of
{ok, Pid} ->
get_process_info(Pid);
error ->
#{error => <<"Ports are not supported yet">>}
end,
epl_json:encode(#{id => list_to_binary(NodeId), info => Info}, <<"node-info">>);
node_info(NodeId) when is_binary(NodeId) ->
node_info(binary_to_list(NodeId)).


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

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

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

handle_cast({subscribe, Pid}, State = #state{subscribers = Subs}) ->
{noreply, State#state{subscribers = [Pid|Subs]}};
handle_cast({unsubscribe, Pid}, State = #state{subscribers = Subs}) ->
{noreply, State#state{subscribers = lists:delete(Pid, Subs)}};
handle_cast(_Msg, State) ->
{noreply, State}.

handle_info({data, _, _}, State = #state{subscribers = Subs}) ->
AppsInfo = command(fun application:info/0),
Apps = lists:foldl(fun({Name, Pid}, Acc) ->
maps:put(Name, generate_sup_tree(Pid), Acc)
end,
#{},
proplists:get_value(running, AppsInfo)),
JSON = epl_json:encode(Apps, <<"apps-info">>),

[Pid ! {data, JSON} || Pid <- Subs],
{noreply, State}.

terminate(_Reason, _State) ->
ok.

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

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

generate_sup_tree({_Name, Pid, worker, _Mods}) ->
worker_node(Pid);
generate_sup_tree({_Name, Pid, supervisor, _Mods}) ->
Children = command(fun supervisor:which_children/1, [Pid]),
sup_node(Pid, generate_children(Children));
generate_sup_tree(undefined) ->
#{};
generate_sup_tree(MasterPid) ->
{SupPid, _SupName} = command(fun application_master:get_child/1, [MasterPid]),
Children = command(fun supervisor:which_children/1, [SupPid]),
sup_node(SupPid, generate_children(Children)).

generate_children(Children) ->
lists:filtermap(
fun({_, undefined, _, _}) ->
false;
(Child) ->
{true, generate_sup_tree(Child)}
end,
Children).

worker_node(Pid) ->
tree_node(Pid, worker, []).

sup_node(Pid, Children) ->
tree_node(Pid, supervisor, Children).

tree_node(Pid, Type, Children) ->
#{id => epl:to_bin(Pid), type => Type, children => Children}.

command(Fun) ->
command(Fun, []).

command(Fun, Args) ->
{ok, Result} = epl_tracer:command(Fun, Args),
Result.

node_id_to_pid(NodeId) when is_list(NodeId) ->
case catch list_to_pid(NodeId) of
{'EXIT',{badarg,_}} ->
error;
Pid ->
{ok, Pid}
end.

get_process_info(Pid) ->
case epl:process_info(Pid) of
{ok, undefined} ->
#{status => exited};
{ok, ProcessInfo} ->
format_process_info(ProcessInfo)
end.

format_process_info(ProcessInfo) ->
lists:foldl(fun ({K, V}, Acc) ->
FormattedVal = format_process_info(K, V),
maps:put(K, FormattedVal, Acc)
end, #{}, ProcessInfo).

format_process_info(current_function, MFA) ->
format_mfa(MFA);
format_process_info(initial_call, MFA) ->
format_mfa(MFA);
format_process_info(dictionary, Dict) ->
lists:foldl(fun ({K, V}, Acc) ->
FormattedVal = format_dictionary_item(K, V),
maps:put(K, FormattedVal, Acc)
end, #{}, Dict);
format_process_info(_K, V) ->
list_to_binary(io_lib:format("~p", [V])).

format_dictionary_item('$initial_call', MFA) ->
format_mfa(MFA);
format_dictionary_item('$ancestors', V) ->
V;
format_dictionary_item(_K, V) ->
list_to_binary(io_lib:format("~p", [V])).

format_mfa({M, F, A}) ->
#{<<"module">> => M, <<"function">> => F, <<"arity">> => A}.
59 changes: 59 additions & 0 deletions apps/epl_st/src/epl_st_EPL.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
%%%-------------------------------------------------------------------
%%% @doc
%%% Websocket handler returning supervision trees data to web client
%%% @end
%%%-------------------------------------------------------------------

-module(epl_st_EPL).

-behaviour(cowboy_websocket_handler).

%% EPL plugin callbacks
-export([start_link/1,
init/1]).

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

%%%===================================================================
%%% EPL plugin callbacks
%%%===================================================================
start_link(_Options) ->
{ok, spawn(fun() -> receive _ -> ok end end)}.

init(_Options) ->
MenuItem = <<"<li class=\"glyphicons share_alt\">",
"<a href=\"/epl_st/index.html\"><i></i>",
"<span>Supervision trees</span></a>",
"</li>">>,
Author = <<"Erlang Lab">>,
{ok, [{menu_item, MenuItem}, {author, Author}]}.

%%%===================================================================
%%% cowboy_websocket_handler callbacks
%%%===================================================================
init({tcp, http}, _Req, _Opts) ->
epl_st:subscribe(),
{upgrade, protocol, cowboy_websocket}.

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

websocket_handle({text, Id}, Req, State) ->
Data = epl_st:node_info(Id),
{reply, {text, Data}, 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_st:unsubscribe(),
ok.
26 changes: 26 additions & 0 deletions apps/epl_st/src/epl_st_app.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
%%%-------------------------------------------------------------------
%% @doc epl_st application callback module
%% @end
%%%-------------------------------------------------------------------

-module(epl_st_app).

-behaviour(application).

%% Application callbacks
-export([start/2, stop/1]).

%%====================================================================
%% API
%%====================================================================

start(_StartType, _StartArgs) ->
epl_st_sup:start_link().

%%--------------------------------------------------------------------
stop(_State) ->
ok.

%%====================================================================
%% Internal functions
%%====================================================================
35 changes: 35 additions & 0 deletions apps/epl_st/src/epl_st_sup.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
%%%-------------------------------------------------------------------
%% @doc epl_st top level supervisor
%% @end
%%%-------------------------------------------------------------------

-module(epl_st_sup).

-behaviour(supervisor).

%% API
-export([start_link/0]).

%% Supervisor callbacks
-export([init/1]).

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

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

%%====================================================================
%% Supervisor callbacks
%%====================================================================

init([]) ->
{ok, {{one_for_all, 0, 1}, children()}}.

children() ->
[{epl_st, {epl_st, start_link, []}, permanent, 5000, worker, [epl_st]}].

%%====================================================================
%% Internal functions
%%====================================================================
2 changes: 1 addition & 1 deletion rebar.config
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{sub_dirs, ["apps/epl", "rel"]}.
{sub_dirs, ["apps/epl", "apps/epl_st", "rel"]}.

0 comments on commit 8c7f242

Please sign in to comment.