aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/observer/src')
-rw-r--r--lib/observer/src/Makefile2
-rw-r--r--lib/observer/src/cdv_bin_cb.erl6
-rw-r--r--lib/observer/src/cdv_detail_wx.erl10
-rw-r--r--lib/observer/src/cdv_dist_cb.erl6
-rw-r--r--lib/observer/src/cdv_ets_cb.erl73
-rw-r--r--lib/observer/src/cdv_fun_cb.erl2
-rw-r--r--lib/observer/src/cdv_gen_cb.erl4
-rw-r--r--lib/observer/src/cdv_html_wx.erl2
-rw-r--r--lib/observer/src/cdv_mod_cb.erl6
-rw-r--r--lib/observer/src/cdv_port_cb.erl8
-rw-r--r--lib/observer/src/cdv_proc_cb.erl11
-rw-r--r--lib/observer/src/cdv_sched_cb.erl117
-rw-r--r--lib/observer/src/cdv_term_cb.erl4
-rw-r--r--lib/observer/src/cdv_timer_cb.erl2
-rw-r--r--lib/observer/src/cdv_virtual_list_wx.erl97
-rw-r--r--lib/observer/src/cdv_wx.erl10
-rw-r--r--lib/observer/src/crashdump_viewer.erl205
-rw-r--r--lib/observer/src/crashdump_viewer.hrl28
-rw-r--r--lib/observer/src/observer.app.src4
-rw-r--r--lib/observer/src/observer_alloc_wx.erl256
-rw-r--r--lib/observer/src/observer_html_lib.erl10
-rw-r--r--lib/observer/src/observer_lib.erl13
-rw-r--r--lib/observer/src/observer_perf_wx.erl273
-rw-r--r--lib/observer/src/observer_pro_wx.erl2
-rw-r--r--lib/observer/src/observer_procinfo.erl78
-rw-r--r--lib/observer/src/observer_sys_wx.erl87
-rw-r--r--lib/observer/src/observer_tv_wx.erl14
-rw-r--r--lib/observer/src/observer_wx.erl135
-rw-r--r--lib/observer/src/ttb.erl2
29 files changed, 1160 insertions, 307 deletions
diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile
index c120865213..8c6606d0a6 100644
--- a/lib/observer/src/Makefile
+++ b/lib/observer/src/Makefile
@@ -51,6 +51,7 @@ MODULES= \
cdv_multi_wx \
cdv_port_cb \
cdv_proc_cb \
+ cdv_sched_cb \
cdv_table_wx \
cdv_term_cb \
cdv_timer_cb \
@@ -61,6 +62,7 @@ MODULES= \
etop_txt \
observer \
observer_app_wx \
+ observer_alloc_wx \
observer_html_lib \
observer_lib \
observer_perf_wx \
diff --git a/lib/observer/src/cdv_bin_cb.erl b/lib/observer/src/cdv_bin_cb.erl
index d5fbceff1e..8b427e92b7 100644
--- a/lib/observer/src/cdv_bin_cb.erl
+++ b/lib/observer/src/cdv_bin_cb.erl
@@ -17,14 +17,14 @@
%% %CopyrightEnd%
-module(cdv_bin_cb).
--export([get_details/1,
+-export([get_details/2,
detail_pages/0]).
%% Callbacks for cdv_detail_wx
-get_details({Type, {T,Key}}) ->
+get_details({Type, {T,Key}}, _) ->
[{Key,Term}] = ets:lookup(T,Key),
{ok,{"Expanded Binary", {Type, Term}, []}};
-get_details({cdv, Id}) ->
+get_details({cdv, Id}, _) ->
{ok,Bin} = crashdump_viewer:expand_binary(Id),
{ok,{"Expanded Binary", {cvd, Bin}, []}}.
diff --git a/lib/observer/src/cdv_detail_wx.erl b/lib/observer/src/cdv_detail_wx.erl
index dc93507a36..ec0d877d87 100644
--- a/lib/observer/src/cdv_detail_wx.erl
+++ b/lib/observer/src/cdv_detail_wx.erl
@@ -19,7 +19,7 @@
-behaviour(wx_object).
--export([start_link/3]).
+-export([start_link/4]).
-export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3,
handle_call/3, handle_info/2]).
@@ -38,13 +38,13 @@
-define(ID_NOTEBOOK, 604).
%% Detail view
-start_link(Id, ParentFrame, Callback) ->
- wx_object:start_link(?MODULE, [Id, ParentFrame, Callback, self()], []).
+start_link(Id, Data, ParentFrame, Callback) ->
+ wx_object:start_link(?MODULE, [Id, Data, ParentFrame, Callback, self()], []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-init([Id, ParentFrame, Callback, Parent]) ->
- case Callback:get_details(Id) of
+init([Id, Data, ParentFrame, Callback, Parent]) ->
+ case Callback:get_details(Id, Data) of
{ok,Details} ->
init(Id,ParentFrame,Callback,Parent,Details);
{yes_no, Info, Fun} ->
diff --git a/lib/observer/src/cdv_dist_cb.erl b/lib/observer/src/cdv_dist_cb.erl
index f7e6c9aded..f45fb1f524 100644
--- a/lib/observer/src/cdv_dist_cb.erl
+++ b/lib/observer/src/cdv_dist_cb.erl
@@ -21,7 +21,7 @@
col_spec/0,
get_info/1,
get_detail_cols/1,
- get_details/1,
+ get_details/2,
detail_pages/0,
format/1]).
@@ -55,10 +55,10 @@ get_info(_) ->
{Info,TW}.
get_detail_cols(_) ->
- {[?COL_CH,?COL_CTRL],true}.
+ {[{node, ?COL_CH},{port,?COL_CTRL}],true}.
%% Callbacks for cdv_detail_wx
-get_details(Id) ->
+get_details(Id, _) ->
case crashdump_viewer:node_info(Id) of
{ok,Info,TW} ->
Proplist = crashdump_viewer:to_proplist(record_info(fields,nod),Info),
diff --git a/lib/observer/src/cdv_ets_cb.erl b/lib/observer/src/cdv_ets_cb.erl
index 2a5c170e58..371c7f0b32 100644
--- a/lib/observer/src/cdv_ets_cb.erl
+++ b/lib/observer/src/cdv_ets_cb.erl
@@ -20,7 +20,10 @@
-export([col_to_elem/1,
col_spec/0,
get_info/1,
- get_detail_cols/1]).
+ get_details/2,
+ get_detail_cols/1,
+ detail_pages/0
+ ]).
-include_lib("wx/include/wx.hrl").
-include("crashdump_viewer.hrl").
@@ -41,7 +44,7 @@ col_to_elem(?COL_ID) -> #ets_table.id;
col_to_elem(?COL_NAME) -> #ets_table.name;
col_to_elem(?COL_SLOT) -> #ets_table.slot;
col_to_elem(?COL_OWNER) -> #ets_table.pid;
-col_to_elem(?COL_TYPE) -> #ets_table.type;
+col_to_elem(?COL_TYPE) -> #ets_table.data_type;
col_to_elem(?COL_BUCK) -> #ets_table.buckets;
col_to_elem(?COL_OBJ) -> #ets_table.size;
col_to_elem(?COL_MEM) -> #ets_table.memory.
@@ -50,18 +53,68 @@ col_spec() ->
[{"Id", ?wxLIST_FORMAT_LEFT, 200},
{"Name", ?wxLIST_FORMAT_LEFT, 200},
{"Slot", ?wxLIST_FORMAT_RIGHT, 50},
- {"Owner", ?wxLIST_FORMAT_CENTRE, 90},
- {"Buckets", ?wxLIST_FORMAT_RIGHT, 50},
- {"Objects", ?wxLIST_FORMAT_RIGHT, 50},
- {"Memory", ?wxLIST_FORMAT_RIGHT, 80},
- {"Type", ?wxLIST_FORMAT_LEFT, 50}
+ {"Owner", ?wxLIST_FORMAT_CENTRE, 120},
+ {"Objects", ?wxLIST_FORMAT_RIGHT, 80},
+ {"Memory", ?wxLIST_FORMAT_RIGHT, 80}
+% {"Type", ?wxLIST_FORMAT_LEFT, 50}
].
get_info(Owner) ->
{ok,Info,TW} = crashdump_viewer:ets_tables(Owner),
{Info,TW}.
+%% Callbacks for cdv_detail_wx
+get_details(_Id, not_found) ->
+ Info = "The table you are searching for could not be found.",
+ {info,Info};
+get_details(Id, Data) ->
+ Proplist = crashdump_viewer:to_proplist(record_info(fields,ets_table),Data),
+ {ok,{"Table:" ++ Id,Proplist,""}}.
+
get_detail_cols(all) ->
- {[?COL_OWNER],false};
-get_detail_cols(_) ->
- {[],false}.
+ {[{ets, ?COL_ID}, {process, ?COL_OWNER}],true};
+get_detail_cols(_W) ->
+ {[],true}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%
+
+detail_pages() ->
+ [{"Table Information", fun init_gen_page/2}].
+
+init_gen_page(Parent, Info0) ->
+ Fields = info_fields(),
+ Details = proplists:get_value(details, Info0),
+ Info = if is_map(Details) -> Info0 ++ maps:to_list(Details);
+ true -> Info0
+ end,
+ cdv_info_wx:start_link(Parent,{Fields,Info,[]}).
+
+%%% Internal
+info_fields() ->
+ [{"Overview",
+ [{"Id", id},
+ {"Name", name},
+ {"Slot", slot},
+ {"Owner", owner},
+ {"Data Structure", data_type}
+ ]},
+ {"Settings",
+ [{"Type", type},
+ {"Protection", protection},
+ {"Compressed", compressed},
+ {"Fixed", fixed},
+ {"Lock write concurrency", write_c},
+ {"Lock read concurrency", read_c}
+ ]},
+ {"Memory Usage",
+ [{"Buckets", buckets},
+ {"Size", size},
+ {"Memory", memory},
+ {"Min Chain Length", chain_min},
+ {"Avg Chain Length", chain_avg},
+ {"Max Chain Length", chain_max},
+ {"Chain Length Std Dev", chain_stddev},
+ {"Chain Length Expected Std Dev", chain_exp_stddev}
+ ]}
+ ].
diff --git a/lib/observer/src/cdv_fun_cb.erl b/lib/observer/src/cdv_fun_cb.erl
index 689ef0e3bb..067377254a 100644
--- a/lib/observer/src/cdv_fun_cb.erl
+++ b/lib/observer/src/cdv_fun_cb.erl
@@ -55,4 +55,4 @@ get_info(_) ->
{Info,TW}.
get_detail_cols(_) ->
- {[?COL_MOD],false}.
+ {[{module, ?COL_MOD}],false}.
diff --git a/lib/observer/src/cdv_gen_cb.erl b/lib/observer/src/cdv_gen_cb.erl
index 6be717d76d..aa5e7c5182 100644
--- a/lib/observer/src/cdv_gen_cb.erl
+++ b/lib/observer/src/cdv_gen_cb.erl
@@ -42,4 +42,6 @@ info_fields() ->
{"Processes",num_procs},
{"ETS tables",num_ets},
{"Timers",num_timers},
- {"Funs",num_fun}]}].
+ {"Funs",num_fun},
+ {"Calling Thread", thread}
+ ]}].
diff --git a/lib/observer/src/cdv_html_wx.erl b/lib/observer/src/cdv_html_wx.erl
index b79c647f63..6d19589f5d 100644
--- a/lib/observer/src/cdv_html_wx.erl
+++ b/lib/observer/src/cdv_html_wx.erl
@@ -126,7 +126,7 @@ expand(Id,Callback,#state{expand_wins=Opened0}=State) ->
Opened =
case lists:keyfind(Id,1,Opened0) of
false ->
- EW = cdv_detail_wx:start_link(Id,State#state.panel,Callback),
+ EW = cdv_detail_wx:start_link(Id,[],State#state.panel,Callback),
wx_object:get_pid(EW) ! active,
[{Id,EW}|Opened0];
{_,EW} ->
diff --git a/lib/observer/src/cdv_mod_cb.erl b/lib/observer/src/cdv_mod_cb.erl
index e829ff4fca..8d33f9da9d 100644
--- a/lib/observer/src/cdv_mod_cb.erl
+++ b/lib/observer/src/cdv_mod_cb.erl
@@ -21,7 +21,7 @@
col_spec/0,
get_info/1,
get_detail_cols/1,
- get_details/1,
+ get_details/2,
detail_pages/0,
format/1]).
@@ -49,10 +49,10 @@ get_info(_) ->
{Info,TW}.
get_detail_cols(_) ->
- {[?COL_ID],true}.
+ {[{module, ?COL_ID}],true}.
%% Callbacks for cdv_detail_wx
-get_details(Id) ->
+get_details(Id, _) ->
{ok,Info,TW} = crashdump_viewer:loaded_mod_details(Id),
Proplist = crashdump_viewer:to_proplist(record_info(fields,loaded_mod),Info),
Title = io_lib:format("~s",[Info#loaded_mod.mod]),
diff --git a/lib/observer/src/cdv_port_cb.erl b/lib/observer/src/cdv_port_cb.erl
index 08488d3e34..409431218b 100644
--- a/lib/observer/src/cdv_port_cb.erl
+++ b/lib/observer/src/cdv_port_cb.erl
@@ -21,7 +21,7 @@
col_spec/0,
get_info/1,
get_detail_cols/1,
- get_details/1,
+ get_details/2,
detail_pages/0,
format/1]).
@@ -57,10 +57,10 @@ get_info(_) ->
{Info,TW}.
get_detail_cols(_) ->
- {[?COL_ID,?COL_CONN],true}.
+ {[{port, ?COL_ID},{process, ?COL_CONN}],true}.
%% Callbacks for cdv_detail_wx
-get_details(Id) ->
+get_details(Id, _Data) ->
case crashdump_viewer:port(Id) of
{ok,Info,TW} ->
Proplist =
@@ -70,7 +70,7 @@ get_details(Id) ->
Info = "The port you are searching for was residing on "
"a remote node. No port information is available. "
"Show information about the remote node?",
- Fun = fun() -> cdv_virtual_list_wx:start_detail_win(NodeId) end,
+ Fun = fun() -> cdv_virtual_list_wx:start_detail_win(NodeId, node) end,
{yes_no, Info, Fun};
{error,not_found} ->
Info = "The port you are searching for could not be found.",
diff --git a/lib/observer/src/cdv_proc_cb.erl b/lib/observer/src/cdv_proc_cb.erl
index dfc2df9c4c..0af6a9c235 100644
--- a/lib/observer/src/cdv_proc_cb.erl
+++ b/lib/observer/src/cdv_proc_cb.erl
@@ -21,7 +21,7 @@
col_spec/0,
get_info/1,
get_detail_cols/1,
- get_details/1,
+ get_details/2,
detail_pages/0]).
-include_lib("wx/include/wx.hrl").
@@ -57,10 +57,10 @@ get_info(_) ->
{Info,TW}.
get_detail_cols(_) ->
- {[?COL_ID],true}.
+ {[{process, ?COL_ID}],true}.
%% Callbacks for cdv_detail_wx
-get_details(Id) ->
+get_details(Id, _) ->
case crashdump_viewer:proc_details(Id) of
{ok,Info,TW} ->
%% The following table is used by observer_html_lib
@@ -76,7 +76,7 @@ get_details(Id) ->
Info = "The process you are searching for was residing on "
"a remote node. No process information is available. "
"Show information about the remote node?",
- Fun = fun() -> cdv_virtual_list_wx:start_detail_win(NodeId) end,
+ Fun = fun() -> cdv_virtual_list_wx:start_detail_win(NodeId, port) end,
{yes_no, Info, Fun};
{error,not_found} ->
Info = "The process you are searching for could not be found.",
@@ -126,10 +126,13 @@ info_fields() ->
{dynamic, current_func},
{"Registered Name", name},
{"Status", state},
+ {"Internal State", int_state},
{"Started", start_time},
{"Parent", {click,parent}},
{"Message Queue Len",msg_q_len},
+ {"Run queue", run_queue},
{"Reductions", reds},
+
{"Program counter", prog_count},
{"Continuation pointer",cp},
{"Arity",arity}]},
diff --git a/lib/observer/src/cdv_sched_cb.erl b/lib/observer/src/cdv_sched_cb.erl
new file mode 100644
index 0000000000..6ef4886c5e
--- /dev/null
+++ b/lib/observer/src/cdv_sched_cb.erl
@@ -0,0 +1,117 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+-module(cdv_sched_cb).
+
+-export([col_to_elem/1,
+ col_spec/0,
+ get_info/1,
+ get_details/2,
+ get_detail_cols/1,
+ detail_pages/0
+ ]).
+
+-include_lib("wx/include/wx.hrl").
+-include("crashdump_viewer.hrl").
+
+%% Columns
+-define(COL_ID, 0).
+-define(COL_PROC, ?COL_ID+1).
+-define(COL_PORT, ?COL_PROC+1).
+-define(COL_RQL, ?COL_PORT+1).
+-define(COL_PQL, ?COL_RQL+1).
+
+%% Callbacks for cdv_virtual_list_wx
+col_to_elem(id) -> col_to_elem(?COL_ID);
+col_to_elem(?COL_ID) -> #sched.name;
+col_to_elem(?COL_PROC) -> #sched.process;
+col_to_elem(?COL_PORT) -> #sched.port;
+col_to_elem(?COL_RQL) -> #sched.run_q;
+col_to_elem(?COL_PQL) -> #sched.port_q.
+
+col_spec() ->
+ [{"Id", ?wxLIST_FORMAT_RIGHT, 50},
+ {"Current Process", ?wxLIST_FORMAT_CENTER, 130},
+ {"Current Port", ?wxLIST_FORMAT_CENTER, 130},
+ {"Run Queue Length", ?wxLIST_FORMAT_RIGHT, 180},
+ {"Port Queue Length", ?wxLIST_FORMAT_RIGHT, 180}].
+
+get_info(_) ->
+ {ok,Info,TW} = crashdump_viewer:schedulers(),
+ {Info,TW}.
+
+get_details(_Id, not_found) ->
+ Info = "The scheduler you are searching for could not be found.",
+ {info,Info};
+get_details(Id, Data) ->
+ Proplist = crashdump_viewer:to_proplist(record_info(fields,sched),Data),
+ {ok,{"Scheduler: " ++ Id,Proplist,""}}.
+
+get_detail_cols(all) ->
+ {[{sched, ?COL_ID}, {process, ?COL_PROC}, {process, ?COL_PORT}],true};
+get_detail_cols(_) ->
+ {[],false}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%
+
+detail_pages() ->
+ [{"Scheduler Information", fun init_gen_page/2}].
+
+init_gen_page(Parent, Info0) ->
+ Fields = info_fields(),
+ Details = proplists:get_value(details, Info0),
+ Info = if is_map(Details) -> Info0 ++ maps:to_list(Details);
+ true -> Info0
+ end,
+ cdv_info_wx:start_link(Parent,{Fields,Info,[]}).
+
+%%% Internal
+info_fields() ->
+ [{"Scheduler Overview",
+ [{"Id", id},
+ {"Current Process",process},
+ {"Current Port", port},
+ {"Sleep Info Flags", sleep_info},
+ {"Sleep Aux Work", sleep_aux}
+ ]},
+ {"Run Queues",
+ [{"Flags", runq_flags},
+ {"Priority Max Length", runq_max},
+ {"Priority High Length", runq_high},
+ {"Priority Normal Length", runq_norm},
+ {"Priority Low Length", runq_low},
+ {"Port Length", port_q}
+ ]},
+ {"Current Process",
+ [{"State", currp_state},
+ {"Internal State", currp_int_state},
+ {"Program Counter", currp_prg_cnt},
+ {"CP", currp_cp},
+ {"Stack", {currp_stack, 0}},
+ {" ", {currp_stack, 1}},
+ {" ", {currp_stack, 2}},
+ {" ", {currp_stack, 3}},
+ {" ", {currp_stack, 4}},
+ {" ", {currp_stack, 5}},
+ {" ", {currp_stack, 6}},
+ {" ", {currp_stack, 7}},
+ {" ", {currp_stack, 8}},
+ {" ", {currp_stack, 9}},
+ {" ", {currp_stack, 10}},
+ {" ", {currp_stack, 11}}
+ ]}
+ ].
diff --git a/lib/observer/src/cdv_term_cb.erl b/lib/observer/src/cdv_term_cb.erl
index 4451045012..6db6d54514 100644
--- a/lib/observer/src/cdv_term_cb.erl
+++ b/lib/observer/src/cdv_term_cb.erl
@@ -17,11 +17,11 @@
%% %CopyrightEnd%
-module(cdv_term_cb).
--export([get_details/1,
+-export([get_details/2,
detail_pages/0]).
%% Callbacks for cdv_detail_wx
-get_details({Type, {T,Key}}) ->
+get_details({Type, {T,Key}}, _) ->
[{Key,Term}] = ets:lookup(T,Key),
{ok,{"Expanded Term", {Type,[Term, T]}, []}}.
diff --git a/lib/observer/src/cdv_timer_cb.erl b/lib/observer/src/cdv_timer_cb.erl
index d44592cf18..b4564941ea 100644
--- a/lib/observer/src/cdv_timer_cb.erl
+++ b/lib/observer/src/cdv_timer_cb.erl
@@ -49,6 +49,6 @@ get_info(Owner) ->
{Info,TW}.
get_detail_cols(all) ->
- {[?COL_OWNER],false};
+ {[{process, ?COL_OWNER}],false};
get_detail_cols(_) ->
{[],false}.
diff --git a/lib/observer/src/cdv_virtual_list_wx.erl b/lib/observer/src/cdv_virtual_list_wx.erl
index bfe115a42e..c0bc7018cb 100644
--- a/lib/observer/src/cdv_virtual_list_wx.erl
+++ b/lib/observer/src/cdv_virtual_list_wx.erl
@@ -19,7 +19,8 @@
-behaviour(wx_object).
--export([start_link/2, start_link/3, start_detail_win/1]).
+-export([start_link/2, start_link/3,
+ start_detail_win/1, start_detail_win/2]).
%% wx_object callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
@@ -65,22 +66,31 @@ start_link(ParentWin, Callback, Owner) ->
wx_object:start_link(?MODULE, [ParentWin, Callback, Owner], []).
start_detail_win(Id) ->
- Callback =
- case Id of
- "<"++_ ->
- cdv_proc_cb;
- "#Port"++_ ->
- cdv_port_cb;
- _ ->
- case catch list_to_integer(Id) of
- NodeId when is_integer(NodeId) ->
- cdv_dist_cb;
- _ ->
- cdv_mod_cb
- end
- end,
- start_detail_win(Callback,Id).
-start_detail_win(Callback,Id) ->
+ case Id of
+ "<"++_ ->
+ start_detail_win(Id, process);
+ "#Port"++_ ->
+ start_detail_win(Id, port);
+ _ ->
+ io:format("cdv: unknown identifier: ~p~n",[Id]),
+ ignore
+ end.
+
+start_detail_win(Id, process) ->
+ start_detail_win_2(cdv_proc_cb, Id);
+start_detail_win(Id, port) ->
+ start_detail_win_2(cdv_port_cb, Id);
+start_detail_win(Id, node) ->
+ start_detail_win_2(cdv_dist_cb, Id);
+start_detail_win(Id, module) ->
+ start_detail_win_2(cdv_mod_cb, Id);
+start_detail_win(Id, ets) ->
+ start_detail_win_2(cdv_ets_cb, Id);
+start_detail_win(Id, sched) ->
+ start_detail_win_2(cdv_sched_cb, Id).
+
+
+start_detail_win_2(Callback,Id) ->
wx_object:cast(Callback,{start_detail_win,Id}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -158,15 +168,14 @@ create_list_box(Panel, Holder, Callback, Owner) ->
do_start_detail_win(undefined, State) ->
State;
do_start_detail_win(Id, #state{panel=Panel,detail_wins=Opened,
- callback=Callback}=State) ->
+ holder=Holder,callback=Callback}=State) ->
NewOpened =
case lists:keyfind(Id, 1, Opened) of
false ->
- case cdv_detail_wx:start_link(Id, Panel, Callback) of
- {error, _} ->
- Opened;
- IW ->
- [{Id, IW} | Opened]
+ Data = call(Holder, {get_data, self(), Id}),
+ case cdv_detail_wx:start_link(Id, Data, Panel, Callback) of
+ {error, _} -> Opened;
+ IW -> [{Id, IW} | Opened]
end;
{_, IW} ->
wxFrame:raise(IW),
@@ -247,8 +256,8 @@ handle_event(#wx{id=MenuId,
event=#wxCommand{type = command_menu_selected}},
#state{menu_items=MenuItems} = State) ->
case lists:keyfind(MenuId,1,MenuItems) of
- {MenuId,Id} ->
- start_detail_win(Id);
+ {MenuId,Type,Id} ->
+ start_detail_win(Id, Type);
false ->
ok
end,
@@ -265,7 +274,7 @@ handle_event(#wx{event=#wxList{type=command_list_item_right_click,
Menu = wxMenu:new(),
MenuItems =
lists:flatmap(
- fun(Col) ->
+ fun({Type, Col}) ->
MenuId = ?ID_DETAILS + Col,
ColText = call(Holder, {get_row, self(), Row, Col}),
case ColText of
@@ -273,14 +282,15 @@ handle_event(#wx{event=#wxList{type=command_list_item_right_click,
_ ->
What =
case catch list_to_integer(ColText) of
- NodeId when is_integer(NodeId) ->
+ NodeId when is_integer(NodeId),
+ Type =:= node ->
"node " ++ ColText;
_ ->
ColText
end,
Text = "Properties for " ++ What,
wxMenu:append(Menu, MenuId, Text),
- [{MenuId,ColText}]
+ [{MenuId,Type,ColText}]
end
end,
MenuCols),
@@ -300,9 +310,14 @@ handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}},
handle_event(#wx{event=#wxList{type=command_list_item_activated,
itemIndex=Row}},
- #state{holder=Holder} = State) ->
- Id = call(Holder, {get_row, self(), Row, id}),
- start_detail_win(Id),
+ #state{holder=Holder, menu_cols=MenuCols} = State) ->
+ case MenuCols of
+ [{Type, _}|_] ->
+ Id = call(Holder, {get_row, self(), Row, id}),
+ start_detail_win(Id, Type);
+ _ ->
+ ignore
+ end,
{noreply, State};
handle_event(Event, State) ->
@@ -346,7 +361,7 @@ init_table_holder(Parent, Attrs, Callback, InfoList0) ->
attrs=Attrs,
callback=Callback}).
-table_holder(#holder{callback=Callback, attrs=Attrs}=S0) ->
+table_holder(#holder{callback=Callback, attrs=Attrs, info=Info}=S0) ->
receive
_M={get_row, From, Row, Col} ->
%% erlang:display(_M),
@@ -360,6 +375,9 @@ table_holder(#holder{callback=Callback, attrs=Attrs}=S0) ->
%% erlang:display(_M),
State = change_sort(Callback:col_to_elem(Col), S0),
table_holder(State);
+ _M={get_data, From, Id} ->
+ search_id(From, Id, Callback, Info),
+ table_holder(S0);
stop ->
ok;
What ->
@@ -367,6 +385,21 @@ table_holder(#holder{callback=Callback, attrs=Attrs}=S0) ->
table_holder(S0)
end.
+search_id(From, Id, Callback, Info) ->
+ Find = fun(_, RowInfo, _) ->
+ search_id(Callback, RowInfo, Id)
+ end,
+ Res = try array:foldl(Find, not_found, Info)
+ catch Data -> Data end,
+ From ! {self(), Res},
+ ok.
+
+search_id(Callback, RowInfo, Id) ->
+ case observer_lib:to_str(get_cell_data(Callback, id, RowInfo)) of
+ Id -> throw(RowInfo);
+ _Str -> not_found
+ end.
+
change_sort(Col, S0=#holder{parent=Parent, info=Info0, sort=Sort0}) ->
NRows = array:size(Info0),
InfoList0 = array:to_list(Info0),
diff --git a/lib/observer/src/cdv_wx.erl b/lib/observer/src/cdv_wx.erl
index 26df60b0a6..ec0c652a27 100644
--- a/lib/observer/src/cdv_wx.erl
+++ b/lib/observer/src/cdv_wx.erl
@@ -44,6 +44,7 @@
-define(PORT_STR, "Ports").
-define(ETS_STR, "ETS Tables").
-define(TIMER_STR, "Timers").
+-define(SCHEDULER_STR, "Schedulers").
-define(FUN_STR, "Funs").
-define(ATOM_STR, "Atoms").
-define(DIST_STR, "Nodes").
@@ -66,6 +67,7 @@
port_panel,
ets_panel,
timer_panel,
+ sched_panel,
fun_panel,
atom_panel,
dist_panel,
@@ -171,6 +173,9 @@ setup(#state{frame=Frame, notebook=Notebook}=State) ->
%% Timer Panel
TimerPanel = add_page(Notebook, ?TIMER_STR, cdv_virtual_list_wx,cdv_timer_cb),
+ %% Scheduler Panel
+ SchedPanel = add_page(Notebook, ?SCHEDULER_STR, cdv_virtual_list_wx, cdv_sched_cb),
+
%% Fun Panel
FunPanel = add_page(Notebook, ?FUN_STR, cdv_virtual_list_wx, cdv_fun_cb),
@@ -202,6 +207,7 @@ setup(#state{frame=Frame, notebook=Notebook}=State) ->
port_panel = PortPanel,
ets_panel = EtsPanel,
timer_panel = TimerPanel,
+ sched_panel = SchedPanel,
fun_panel = FunPanel,
atom_panel = AtomPanel,
dist_panel = DistPanel,
@@ -335,7 +341,8 @@ check_page_title(Notebook) ->
get_active_pid(#state{notebook=Notebook, gen_panel=Gen, pro_panel=Pro,
port_panel=Ports, ets_panel=Ets, timer_panel=Timers,
fun_panel=Funs, atom_panel=Atoms, dist_panel=Dist,
- mod_panel=Mods, mem_panel=Mem, int_panel=Int
+ mod_panel=Mods, mem_panel=Mem, int_panel=Int,
+ sched_panel=Sched
}) ->
Panel = case check_page_title(Notebook) of
?GEN_STR -> Gen;
@@ -343,6 +350,7 @@ get_active_pid(#state{notebook=Notebook, gen_panel=Gen, pro_panel=Pro,
?PORT_STR -> Ports;
?ETS_STR -> Ets;
?TIMER_STR -> Timers;
+ ?SCHEDULER_STR -> Sched;
?FUN_STR -> Funs;
?ATOM_STR -> Atoms;
?DIST_STR -> Dist;
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index 99329b94e2..007fc74279 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -63,6 +63,7 @@
allocator_info/0,
hash_tables/0,
index_tables/0,
+ schedulers/0,
expand_binary/1]).
%% Library function
@@ -114,6 +115,7 @@
-define(proc_heap,proc_heap).
-define(proc_messages,proc_messages).
-define(proc_stack,proc_stack).
+-define(scheduler,scheduler).
-define(timer,timer).
-define(visible_node,visible_node).
@@ -267,6 +269,8 @@ hash_tables() ->
call(hash_tables).
index_tables() ->
call(index_tables).
+schedulers() ->
+ call(schedulers).
%%%-----------------------------------------------------------------
%%% Called when a link to a process (Pid) is clicked.
@@ -320,6 +324,8 @@ handle_call(general_info,_From,State=#state{file=File}) ->
"Some information might be missing."];
false -> []
end,
+ ets:insert(cdv_reg_proc_table,
+ {cdv_dump_node_name,GenInfo#general_info.node_name}),
{reply,{ok,GenInfo,TW},State#state{wordsize=WS, num_atoms=NumAtoms}};
handle_call({expand_binary,{Offset,Size,Pos}},_From,State=#state{file=File}) ->
Fd = open(File),
@@ -429,7 +435,11 @@ handle_call(hash_tables,_From,State=#state{file=File}) ->
handle_call(index_tables,_From,State=#state{file=File}) ->
IndexTables=index_tables(File),
TW = truncated_warning([?hash_table,?index_table]),
- {reply,{ok,IndexTables,TW},State}.
+ {reply,{ok,IndexTables,TW},State};
+handle_call(schedulers,_From,State=#state{file=File}) ->
+ Schedulers=schedulers(File),
+ TW = truncated_warning([?scheduler]),
+ {reply,{ok,Schedulers,TW},State}.
@@ -675,9 +685,11 @@ skip(Fd,<<>>) ->
val(Fd) ->
+ val(Fd, "-1").
+val(Fd, NoExist) ->
case get_rest_of_line(Fd) of
- {eof,[]} -> "-1";
- [] -> "-1";
+ {eof,[]} -> NoExist;
+ [] -> NoExist;
{eof,Val} -> Val;
Val -> Val
end.
@@ -926,7 +938,7 @@ general_info(File) ->
N;
[] ->
case lookup_index(?no_distribution) of
- [_] -> "nonode@nohost";
+ [_] -> "'nonode@nohost'";
[] -> "unknown"
end
end,
@@ -965,6 +977,8 @@ get_general_info(Fd,GenInfo) ->
get_general_info(Fd,GenInfo#general_info{taints=Val});
"Atoms" ->
get_general_info(Fd,GenInfo#general_info{num_atoms=val(Fd)});
+ "Calling Thread" ->
+ get_general_info(Fd,GenInfo#general_info{thread=val(Fd)});
"=" ++ _next_tag ->
GenInfo;
Other ->
@@ -1131,6 +1145,10 @@ all_procinfo(Fd,Fun,Proc,WS,LineHead) ->
"arity = " ++ Arity ->
%%! Temporary workaround
get_procinfo(Fd,Fun,Proc#proc{arity=Arity--"\r\n"},WS);
+ "Run queue" ->
+ get_procinfo(Fd,Fun,Proc#proc{run_queue=val(Fd)},WS);
+ "Internal State" ->
+ get_procinfo(Fd,Fun,Proc#proc{int_state=val(Fd)},WS);
"=" ++ _next_tag ->
Proc;
Other ->
@@ -1165,6 +1183,19 @@ parse_pid(Str) ->
{Pid,Rest} = parse_link(Str,[]),
{{Pid,Pid},Rest}.
+parse_monitor("{"++Str) ->
+ %% Named process
+ {Name,Node,Rest1} = parse_name_node(Str,[]),
+ Pid = get_pid_from_name(Name,Node),
+ case parse_link(string:strip(Rest1,left,$,),[]) of
+ {Ref,"}"++Rest2} ->
+ %% Bug in break.c - prints an extra "}" for remote
+ %% nodes... thus the strip
+ {{Pid,"{"++Name++","++Node++"} ("++Ref++")"},
+ string:strip(Rest2,left,$})};
+ {Ref,[]} ->
+ {{Pid,"{"++Name++","++Node++"} ("++Ref++")"},[]}
+ end;
parse_monitor(Str) ->
case parse_link(Str,[]) of
{Pid,","++Rest1} ->
@@ -1186,23 +1217,58 @@ parse_link([],Acc) ->
%% truncated
{lists:reverse(Acc),[]}.
+parse_name_node(","++Rest,Name) ->
+ parse_name_node(Rest,Name,[]);
+parse_name_node([H|T],Name) ->
+ parse_name_node(T,[H|Name]);
+parse_name_node([],Name) ->
+ %% truncated
+ {lists:reverse(Name),[],[]}.
+
+parse_name_node("}"++Rest,Name,Node) ->
+ {lists:reverse(Name),lists:reverse(Node),Rest};
+parse_name_node([H|T],Name,Node) ->
+ parse_name_node(T,Name,[H|Node]);
+parse_name_node([],Name,Node) ->
+ %% truncated
+ {lists:reverse(Name),lists:reverse(Node),[]}.
+
+get_pid_from_name(Name,Node) ->
+ case ets:lookup(cdv_reg_proc_table,cdv_dump_node_name) of
+ [{_,Node}] ->
+ case ets:lookup(cdv_reg_proc_table,Name) of
+ [{_,Pid}] when is_pid(Pid) ->
+ pid_to_list(Pid);
+ _ ->
+ "<unkonwn_pid>"
+ end;
+ _ ->
+ "<unknown_pid_other_node>"
+ end.
+
maybe_other_node(Id) ->
Channel =
case split($.,Id) of
{"<" ++ N, _Rest} ->
N;
{"#Port<" ++ N, _Rest} ->
- N
+ N;
+ {_, []} ->
+ not_found
end,
+ maybe_other_node2(Channel).
+
+maybe_other_node2(not_found) -> not_found;
+maybe_other_node2(Channel) ->
Ms = ets:fun2ms(
- fun({{Tag,Start},Ch}) when Tag=:=?visible_node, Ch=:=Channel ->
+ fun({{Tag,Start},Ch}) when Tag=:=?visible_node, Ch=:=Channel ->
{"Visible Node",Start};
({{Tag,Start},Ch}) when Tag=:=?hidden_node, Ch=:=Channel ->
{"Hidden Node",Start};
- ({{Tag,Start},Ch}) when Tag=:=?not_connected, Ch=:=Channel ->
+ ({{Tag,Start},Ch}) when Tag=:=?not_connected, Ch=:=Channel ->
{"Not Connected Node",Start}
end),
-
+
case ets:select(cdv_dump_index_table,Ms) of
[] ->
not_found;
@@ -1457,7 +1523,7 @@ get_ets_tables(File,Pid,WS) ->
end,
lookup_and_parse_index(File,{?ets,Pid},ParseFun,"ets").
-get_etsinfo(Fd,EtsTable,WS) ->
+get_etsinfo(Fd,EtsTable = #ets_table{details=Ds},WS) ->
case line_head(Fd) of
"Slot" ->
get_etsinfo(Fd,EtsTable#ets_table{slot=list_to_integer(val(Fd))},WS);
@@ -1467,7 +1533,7 @@ get_etsinfo(Fd,EtsTable,WS) ->
get_etsinfo(Fd,EtsTable#ets_table{name=val(Fd)},WS);
"Ordered set (AVL tree), Elements" ->
skip_rest_of_line(Fd),
- get_etsinfo(Fd,EtsTable#ets_table{type="tree",buckets="-"},WS);
+ get_etsinfo(Fd,EtsTable#ets_table{data_type="tree"},WS);
"Buckets" ->
%% A bug in erl_db_hash.c prints a space after the buckets
%% - need to strip the string to make list_to_integer/1 happy.
@@ -1482,9 +1548,42 @@ get_etsinfo(Fd,EtsTable,WS) ->
-1 -> -1; % probably truncated
_ -> Words * WS
end,
- get_etsinfo(Fd,EtsTable#ets_table{memory=Bytes},WS);
+ get_etsinfo(Fd,EtsTable#ets_table{memory={bytes,Bytes}},WS);
"=" ++ _next_tag ->
EtsTable;
+ "Chain Length Min" ->
+ Val = val(Fd),
+ get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_min=>Val}},WS);
+ "Chain Length Avg" ->
+ Val = try list_to_float(string:strip(val(Fd))) catch _:_ -> "-" end,
+ get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_avg=>Val}},WS);
+ "Chain Length Max" ->
+ Val = val(Fd),
+ get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_max=>Val}},WS);
+ "Chain Length Std Dev" ->
+ Val = val(Fd),
+ get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_stddev=>Val}},WS);
+ "Chain Length Expected Std Dev" ->
+ Val = val(Fd),
+ get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_exp_stddev=>Val}},WS);
+ "Fixed" ->
+ Val = val(Fd),
+ get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{fixed=>Val}},WS);
+ "Type" ->
+ Val = val(Fd),
+ get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{data_type=>Val}},WS);
+ "Protection" ->
+ Val = val(Fd),
+ get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{protection=>Val}},WS);
+ "Compressed" ->
+ Val = val(Fd),
+ get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{compressed=>Val}},WS);
+ "Write Concurrency" ->
+ Val = val(Fd),
+ get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{write_c=>Val}},WS);
+ "Read Concurrency" ->
+ Val = val(Fd),
+ get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{read_c=>Val}},WS);
Other ->
unexpected(Fd,Other,"ETS info"),
EtsTable
@@ -2224,6 +2323,89 @@ get_indextableinfo1(Fd,IndexTable) ->
IndexTable
end.
+
+%%-----------------------------------------------------------------
+%% Page with scheduler table information
+schedulers(File) ->
+ case lookup_index(?scheduler) of
+ [] ->
+ [];
+ Schedulers ->
+ Fd = open(File),
+ R = lists:map(fun({Name,Start}) ->
+ get_schedulerinfo(Fd,Name,Start)
+ end,
+ Schedulers),
+ close(Fd),
+ R
+ end.
+
+get_schedulerinfo(Fd,Name,Start) ->
+ pos_bof(Fd,Start),
+ get_schedulerinfo1(Fd,#sched{name=Name}).
+
+get_schedulerinfo1(Fd,Sched=#sched{details=Ds}) ->
+ case line_head(Fd) of
+ "Current Process" ->
+ get_schedulerinfo1(Fd,Sched#sched{process=val(Fd, "None")});
+ "Current Port" ->
+ get_schedulerinfo1(Fd,Sched#sched{port=val(Fd, "None")});
+ "Run Queue Max Length" ->
+ RQMax = list_to_integer(val(Fd)),
+ RQ = RQMax + Sched#sched.run_q,
+ get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_max=>RQMax}});
+ "Run Queue High Length" ->
+ RQHigh = list_to_integer(val(Fd)),
+ RQ = RQHigh + Sched#sched.run_q,
+ get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_high=>RQHigh}});
+ "Run Queue Normal Length" ->
+ RQNorm = list_to_integer(val(Fd)),
+ RQ = RQNorm + Sched#sched.run_q,
+ get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_norm=>RQNorm}});
+ "Run Queue Low Length" ->
+ RQLow = list_to_integer(val(Fd)),
+ RQ = RQLow + Sched#sched.run_q,
+ get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_low=>RQLow}});
+ "Run Queue Port Length" ->
+ RQ = list_to_integer(val(Fd)),
+ get_schedulerinfo1(Fd,Sched#sched{port_q=RQ});
+
+ "Scheduler Sleep Info Flags" ->
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_info=>val(Fd, "None")}});
+ "Scheduler Sleep Info Aux Work" ->
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_aux=>val(Fd, "None")}});
+
+ "Run Queue Flags" ->
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{runq_flags=>val(Fd, "None")}});
+
+ "Current Process State" ->
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_state=>val(Fd)}});
+ "Current Process Internal State" ->
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_int_state=>val(Fd)}});
+ "Current Process Program counter" ->
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_prg_cnt=>val(Fd)}});
+ "Current Process CP" ->
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_cp=>val(Fd)}});
+ "Current Process Limited Stack Trace" ->
+ %% If there shall be last in scheduler information block
+ Sched#sched{details=get_limited_stack(Fd, 0, Ds)};
+ "=" ++ _next_tag ->
+ Sched;
+ Other ->
+ unexpected(Fd,Other,"scheduler information"),
+ Sched
+ end.
+
+get_limited_stack(Fd, N, Ds) ->
+ case val(Fd) of
+ Addr = "0x" ++ _ ->
+ get_limited_stack(Fd, N+1, Ds#{{currp_stack, N} => Addr});
+ "=" ++ _next_tag ->
+ Ds;
+ Line ->
+ get_limited_stack(Fd, N+1, Ds#{{currp_stack, N} => Line})
+ end.
+
%%%-----------------------------------------------------------------
%%% Parse memory in crashdump version 0.1 and newer
%%%
@@ -2526,6 +2708,7 @@ tag_to_atom("proc_dictionary") -> ?proc_dictionary;
tag_to_atom("proc_heap") -> ?proc_heap;
tag_to_atom("proc_messages") -> ?proc_messages;
tag_to_atom("proc_stack") -> ?proc_stack;
+tag_to_atom("scheduler") -> ?scheduler;
tag_to_atom("timer") -> ?timer;
tag_to_atom("visible_node") -> ?visible_node;
tag_to_atom(UnknownTag) ->
diff --git a/lib/observer/src/crashdump_viewer.hrl b/lib/observer/src/crashdump_viewer.hrl
index 0e2eba6dee..9515e74114 100644
--- a/lib/observer/src/crashdump_viewer.hrl
+++ b/lib/observer/src/crashdump_viewer.hrl
@@ -36,7 +36,9 @@
num_fun,
mem_tot,
mem_max,
- instr_info}).
+ instr_info,
+ thread
+ }).
-record(proc,
%% Initial data according to the follwoing:
@@ -85,7 +87,10 @@
old_heap_top,
old_heap_end,
memory,
- stack_dump}).
+ stack_dump,
+ run_queue=?unknown,
+ int_state
+ }).
-record(port,
{id,
@@ -96,15 +101,28 @@
monitors,
controls}).
+-record(sched,
+ {name,
+ process,
+ port,
+ run_q=0,
+ port_q=0,
+ details=#{}
+ }).
+
+
+
-record(ets_table,
{pid,
slot,
id,
name,
- type="hash",
- buckets,
+ data_type="hash",
+ buckets="-",
size,
- memory}).
+ memory,
+ details= #{}
+ }).
-record(timer,
{pid,
diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src
index 97a54cd6f9..c12353f9e1 100644
--- a/lib/observer/src/observer.app.src
+++ b/lib/observer/src/observer.app.src
@@ -37,6 +37,7 @@
cdv_proc_cb,
cdv_table_wx,
cdv_term_cb,
+ cdv_sched_cb,
cdv_timer_cb,
cdv_virtual_list_wx,
cdv_wx,
@@ -44,6 +45,7 @@
etop_tr,
etop_txt,
observer,
+ observer_alloc_wx,
observer_app_wx,
observer_html_lib,
observer_lib,
@@ -63,6 +65,6 @@
{env, []},
{runtime_dependencies, ["wx-1.2","stdlib-2.0","runtime_tools-1.8.14",
"kernel-3.0","inets-5.10","et-1.5",
- "erts-6.0"]}]}.
+ "erts-7.0"]}]}.
diff --git a/lib/observer/src/observer_alloc_wx.erl b/lib/observer/src/observer_alloc_wx.erl
new file mode 100644
index 0000000000..0c4bc9ee4b
--- /dev/null
+++ b/lib/observer/src/observer_alloc_wx.erl
@@ -0,0 +1,256 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+-module(observer_alloc_wx).
+
+-export([start_link/2]).
+
+%% wx_object callbacks
+-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
+ handle_event/2, handle_sync_event/3, handle_cast/2]).
+
+-behaviour(wx_object).
+-include_lib("wx/include/wx.hrl").
+-include("observer_defs.hrl").
+
+-record(state,
+ {
+ offset = 0.0,
+ active = false,
+ parent,
+ windows,
+ data = {0, queue:new()},
+ panel,
+ paint,
+ appmon,
+ async
+ }).
+
+-define(ALLOC_W, 1).
+-define(UTIL_W, 2).
+
+start_link(Notebook, Parent) ->
+ wx_object:start_link(?MODULE, [Notebook, Parent], []).
+
+init([Notebook, Parent]) ->
+ try
+ Panel = wxPanel:new(Notebook),
+ Main = wxBoxSizer:new(?wxVERTICAL),
+ Style = ?wxFULL_REPAINT_ON_RESIZE bor ?wxCLIP_CHILDREN,
+ Carrier = wxPanel:new(Panel, [{winid, ?ALLOC_W}, {style,Style}]),
+ Utilz = wxPanel:new(Panel, [{winid, ?UTIL_W}, {style,Style}]),
+ BorderFlags = ?wxLEFT bor ?wxRIGHT,
+ wxSizer:add(Main, Carrier, [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP},
+ {proportion, 1}, {border, 5}]),
+
+ wxSizer:add(Main, Utilz, [{flag, ?wxEXPAND bor BorderFlags},
+ {proportion, 1}, {border, 5}]),
+
+ MemWin = {MemPanel,_} = create_mem_info(Panel),
+ wxSizer:add(Main, MemPanel, [{flag, ?wxEXPAND bor BorderFlags bor ?wxBOTTOM},
+ {proportion, 1}, {border, 5}]),
+ wxWindow:setSizer(Panel, Main),
+
+ PaintInfo = observer_perf_wx:setup_graph_drawing([Carrier, Utilz]),
+ {Panel, #state{parent=Parent,
+ panel =Panel,
+ windows = {Carrier, Utilz, MemWin},
+ paint=PaintInfo}
+ }
+ catch _:Err ->
+ io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]),
+ {stop, Err}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+handle_event(#wx{event=#wxCommand{type=command_menu_selected}},
+ State = #state{}) ->
+ {noreply, State};
+
+handle_event(Event, _State) ->
+ error({unhandled_event, Event}).
+
+%%%%%%%%%%
+handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_,
+ #state{active=Active, offset=Offset, paint=Paint,
+ windows=Windows, data=Data}) ->
+ %% Sigh workaround bug on MacOSX (Id in paint event is always 0)
+ Id = if Panel =:= element(?ALLOC_W, Windows) -> alloc;
+ Panel =:= element(?UTIL_W, Windows) -> utilz
+ end,
+ observer_perf_wx:refresh_panel(Panel, Id, Offset, Data, Active, Paint),
+ ok.
+%%%%%%%%%%
+handle_call(Event, From, _State) ->
+ error({unhandled_call, Event, From}).
+
+handle_cast(Event, _State) ->
+ error({unhandled_cast, Event}).
+%%%%%%%%%%
+
+handle_info({Key, {promise_reply, {badrpc, _}}}, #state{async=Key} = State) ->
+ {noreply, State#state{active=false, appmon=undefined}};
+
+handle_info({Key, {promise_reply, SysInfo}}, #state{async=Key, data=Data} = State) ->
+ Info = alloc_info(SysInfo),
+ update_alloc(State, Info),
+ {noreply, State#state{offset=0.0, data = add_data(Info, Data), async=undefined}};
+
+handle_info({refresh, Seq, Freq, Node}, #state{panel=Panel, appmon=Node, async=Key} = State) ->
+ wxWindow:refresh(Panel),
+ Next = Seq+1,
+ if
+ Next > Freq, Key =:= undefined ->
+ erlang:send_after(trunc(1000 / Freq), self(), {refresh, 1, Freq, Node}),
+ Req = rpc:async_call(Node, observer_backend, sys_info, []),
+ {noreply, State#state{offset=Seq/Freq, async=Req}};
+ true ->
+ erlang:send_after(trunc(1000 / Freq), self(), {refresh, Next, Freq, Node}),
+ {noreply, State#state{offset=Seq/Freq}}
+ end;
+handle_info({refresh, _Seq, _Freq, _Node}, State) ->
+ {noreply, State};
+
+handle_info({active, Node}, State = #state{parent=Parent, panel=Panel, appmon=Old}) ->
+ create_menus(Parent, []),
+ try
+ Node = Old,
+ wxWindow:refresh(Panel),
+ {noreply, State#state{active=true}}
+ catch _:_ ->
+ SysInfo = observer_wx:try_rpc(Node, observer_backend, sys_info, []),
+ Info = alloc_info(SysInfo),
+ Freq = 6,
+ erlang:send_after(trunc(1000 / Freq), self(), {refresh, 1, Freq, Node}),
+ wxWindow:refresh(Panel),
+ {noreply, State#state{active=true, appmon=Node, offset=0.0,
+ data = add_data(Info, {0, queue:new()})}}
+ end;
+
+handle_info(not_active, State = #state{appmon=_Pid}) ->
+ {noreply, State#state{active=false}};
+
+handle_info({'EXIT', Old, _}, State = #state{appmon=Old}) ->
+ {noreply, State#state{active=false, appmon=undefined}};
+
+handle_info(_Event, State) ->
+ %% io:format("~p:~p: ~p~n",[?MODULE,?LINE,_Event]),
+ {noreply, State}.
+
+terminate(_Event, #state{}) ->
+ ok.
+code_change(_, _, State) ->
+ State.
+
+%%%%%%%%%%
+
+add_data(Stats, {N, Q}) when N > 60 ->
+ {N, queue:drop(queue:in(Stats, Q))};
+add_data(Stats, {N, Q}) ->
+ {N+1, queue:in(Stats, Q)}.
+
+update_alloc(#state{windows={_, _, {_, Grid}}}, Fields) ->
+ Max = wxListCtrl:getItemCount(Grid),
+ Update = fun({Name, BS, CS}, Row) ->
+ (Row >= Max) andalso wxListCtrl:insertItem(Grid, Row, ""),
+ wxListCtrl:setItem(Grid, Row, 0, observer_lib:to_str(Name)),
+ wxListCtrl:setItem(Grid, Row, 1, observer_lib:to_str(BS div 1024)),
+ wxListCtrl:setItem(Grid, Row, 2, observer_lib:to_str(CS div 1024)),
+ Row + 1
+ end,
+ lists:foldl(Update, 0, Fields),
+ Fields.
+
+alloc_info(SysInfo) ->
+ AllocInfo = proplists:get_value(alloc_info, SysInfo, []),
+ alloc_info(AllocInfo, [], 0, 0, true).
+
+alloc_info([{Type,Instances}|Allocators],TypeAcc,TotalBS,TotalCS,IncludeTotal) ->
+ {BS,CS,NewTotalBS,NewTotalCS,NewIncludeTotal} =
+ sum_alloc_instances(Instances,0,0,TotalBS,TotalCS),
+ alloc_info(Allocators,[{Type,BS,CS}|TypeAcc],NewTotalBS,NewTotalCS,
+ IncludeTotal andalso NewIncludeTotal);
+alloc_info([],TypeAcc,TotalBS,TotalCS,IncludeTotal) ->
+ Types = [X || X={_,BS,CS} <- TypeAcc, (BS>0 orelse CS>0)],
+ case IncludeTotal of
+ true ->
+ [{total,TotalBS,TotalCS} | lists:reverse(Types)];
+ false ->
+ lists:reverse(Types)
+ end.
+
+sum_alloc_instances(false,BS,CS,TotalBS,TotalCS) ->
+ {BS,CS,TotalBS,TotalCS,false};
+sum_alloc_instances([{_,_,Data}|Instances],BS,CS,TotalBS,TotalCS) ->
+ {NewBS,NewCS,NewTotalBS,NewTotalCS} =
+ sum_alloc_one_instance(Data,BS,CS,TotalBS,TotalCS),
+ sum_alloc_instances(Instances,NewBS,NewCS,NewTotalBS,NewTotalCS);
+sum_alloc_instances([],BS,CS,TotalBS,TotalCS) ->
+ {BS,CS,TotalBS,TotalCS,true}.
+
+sum_alloc_one_instance([{sbmbcs,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}|
+ Rest],OldBS,OldCS,TotalBS,TotalCS) ->
+ sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS,TotalCS);
+sum_alloc_one_instance([{_,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}|
+ Rest],OldBS,OldCS,TotalBS,TotalCS) ->
+ sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS+BS,TotalCS+CS);
+sum_alloc_one_instance([{_,[{blocks_size,BS},{carriers_size,CS}]}|
+ Rest],OldBS,OldCS,TotalBS,TotalCS) ->
+ sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS+BS,TotalCS+CS);
+sum_alloc_one_instance([_|Rest],BS,CS,TotalBS,TotalCS) ->
+ sum_alloc_one_instance(Rest,BS,CS,TotalBS,TotalCS);
+sum_alloc_one_instance([],BS,CS,TotalBS,TotalCS) ->
+ {BS,CS,TotalBS,TotalCS}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+create_mem_info(Parent) ->
+ Panel = wxPanel:new(Parent),
+ wxWindow:setBackgroundColour(Panel, {255,255,255}),
+ Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES bor ?wxLC_VRULES,
+ Grid = wxListCtrl:new(Panel, [{style, Style}]),
+ Li = wxListItem:new(),
+ AddListEntry = fun({Name, Align, DefSize}, Col) ->
+ wxListItem:setText(Li, Name),
+ wxListItem:setAlign(Li, Align),
+ wxListCtrl:insertColumn(Grid, Col, Li),
+ wxListCtrl:setColumnWidth(Grid, Col, DefSize),
+ Col + 1
+ end,
+ ListItems = [{"Allocator Type", ?wxLIST_FORMAT_LEFT, 200},
+ {"Block size (kB)", ?wxLIST_FORMAT_RIGHT, 150},
+ {"Carrier size (kB)",?wxLIST_FORMAT_RIGHT, 150}],
+ lists:foldl(AddListEntry, 0, ListItems),
+ wxListItem:destroy(Li),
+
+ Sizer = wxBoxSizer:new(?wxVERTICAL),
+ wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxLEFT bor ?wxRIGHT},
+ {border, 5}, {proportion, 1}]),
+ wxWindow:setSizerAndFit(Panel, Sizer),
+ {Panel, Grid}.
+
+
+create_menus(Parent, _) ->
+ MenuEntries =
+ [{"File",
+ [
+ ]}
+ ],
+ observer_wx:create_menus(Parent, MenuEntries).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/observer/src/observer_html_lib.erl b/lib/observer/src/observer_html_lib.erl
index c279218707..df0bc05312 100644
--- a/lib/observer/src/observer_html_lib.erl
+++ b/lib/observer/src/observer_html_lib.erl
@@ -60,7 +60,8 @@ expandable_term_body(Heading,[],_Tab) ->
"StackDump" -> "No stack dump was found";
"Dictionary" -> "No dictionary was found";
"ProcState" -> "Information could not be retrieved,"
- " system messages may not be handled by this process."
+ " system messages may not be handled by this process.";
+ "SaslLog" -> "No log entry was found"
end];
expandable_term_body(Heading,Expanded,Tab) ->
Attr = "BORDER=0 CELLPADDING=0 CELLSPACING=1 WIDTH=100%",
@@ -102,7 +103,10 @@ expandable_term_body(Heading,Expanded,Tab) ->
element(1, lists:mapfoldl(fun(Entry, Even) ->
{proc_state(Tab, Entry,Even),
not Even}
- end, true, Expanded))]);
+ end, true, Expanded))]);
+ "SaslLog" ->
+ table(Attr,
+ [tr("BGCOLOR=white",[td("ALIGN=left", pre(href_proc_port(Expanded)))])]) ;
_ ->
table(Attr,
[tr(
@@ -151,7 +155,7 @@ all_or_expand(_Tab,Term,Str,false)
href_proc_port(lists:flatten(Str));
all_or_expand(Tab,Term,Preview,true)
when not is_binary(Term) ->
- Key = {Key1,Key2,Key3} = now(),
+ Key = {Key1,Key2,Key3} = {erlang:unique_integer([positive]),1,2},
ets:insert(Tab,{Key,Term}),
[href_proc_port(lists:flatten(Preview), false), $\n,
href("TARGET=\"expanded\"",
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 9592ab5977..40a3eb8831 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -173,12 +173,17 @@ fill_info([{Str,SubStructure}|Rest], Data) when is_list(SubStructure) ->
[{Str, fill_info(SubStructure, Data)}|fill_info(Rest,Data)];
fill_info([{Str,Attrib,SubStructure}|Rest], Data) ->
[{Str, Attrib, fill_info(SubStructure, Data)}|fill_info(Rest,Data)];
+fill_info([{Str, Key = {K,N}}|Rest], Data) when is_atom(K), is_integer(N) ->
+ case get_value(Key, Data) of
+ undefined -> [undefined | fill_info(Rest, Data)];
+ Value -> [{Str, Value} | fill_info(Rest, Data)]
+ end;
fill_info([], _) -> [].
-get_value(Key, Data) when is_atom(Key) ->
- proplists:get_value(Key,Data);
get_value(Fun, Data) when is_function(Fun) ->
- Fun(Data).
+ Fun(Data);
+get_value(Key, Data) ->
+ proplists:get_value(Key,Data).
update_info([Fields|Fs], [{_Header, SubStructure}| Rest]) ->
update_info2(Fields, SubStructure),
@@ -269,6 +274,8 @@ to_str(Pid) when is_pid(Pid) ->
pid_to_list(Pid);
to_str(No) when is_integer(No) ->
integer_to_list(No);
+to_str(Float) when is_float(Float) ->
+ io_lib:format("~.3f", [Float]);
to_str(Term) ->
io_lib:format("~w", [Term]).
diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl
index 8173349ed7..4df9218087 100644
--- a/lib/observer/src/observer_perf_wx.erl
+++ b/lib/observer/src/observer_perf_wx.erl
@@ -24,7 +24,8 @@
handle_event/2, handle_sync_event/3, handle_cast/2]).
%% Drawing wrappers for DC and GC areas
--export([haveGC/0,
+-export([setup_graph_drawing/1, refresh_panel/6,
+ haveGC/0,
setPen/2, setFont/3, setBrush/2,
strokeLine/5, strokeLines/2, drawRoundedRectangle/6,
drawText/4, getTextExtent/2]).
@@ -42,13 +43,12 @@
data = {0, queue:new()},
panel,
paint,
- appmon,
- usegc = false
+ appmon
}).
-define(wxGC, wxGraphicsContext).
--record(paint, {font, small, pen, pen2, pens}).
+-record(paint, {font, small, pen, pen2, pens, usegc = false}).
-define(RQ_W, 1).
-define(MEM_W, 2).
@@ -63,14 +63,11 @@ init([Notebook, Parent]) ->
Main = wxBoxSizer:new(?wxVERTICAL),
Style = ?wxFULL_REPAINT_ON_RESIZE bor ?wxCLIP_CHILDREN,
CPU = wxPanel:new(Panel, [{winid, ?RQ_W}, {style,Style}]),
- wxWindow:setBackgroundColour(CPU, ?wxWHITE),
wxSizer:add(Main, CPU, [{flag, ?wxEXPAND bor ?wxALL},
{proportion, 1}, {border, 5}]),
MemIO = wxBoxSizer:new(?wxHORIZONTAL),
MEM = wxPanel:new(Panel, [{winid, ?MEM_W}, {style,Style}]),
- wxWindow:setBackgroundColour(MEM, ?wxWHITE),
IO = wxPanel:new(Panel, [{winid, ?IO_W}, {style,Style}]),
- wxWindow:setBackgroundColour(IO, ?wxWHITE),
wxSizer:add(MemIO, MEM, [{flag, ?wxEXPAND bor ?wxLEFT},
{proportion, 1}, {border, 5}]),
wxSizer:add(MemIO, IO, [{flag, ?wxEXPAND bor ?wxLEFT bor ?wxRIGHT},
@@ -79,53 +76,56 @@ init([Notebook, Parent]) ->
{proportion, 1}, {border, 5}]),
wxWindow:setSizer(Panel, Main),
- wxPanel:connect(CPU, paint, [callback]),
- wxPanel:connect(IO, paint, [callback]),
- wxPanel:connect(MEM, paint, [callback]),
- case os:type() of
- {win32, _} -> %% Ignore erase on windows
- wxPanel:connect(CPU, erase_background, [{callback, fun(_,_) -> ok end}]),
- wxPanel:connect(IO, erase_background, [{callback, fun(_,_) -> ok end}]),
- wxPanel:connect(MEM, erase_background, [{callback, fun(_,_) -> ok end}]);
- _ -> ok
- end,
+ PaintInfo = setup_graph_drawing([CPU, MEM, IO]),
+ process_flag(trap_exit, true),
+ {Panel, #state{parent=Parent,
+ panel =Panel,
+ windows = {CPU, MEM, IO},
+ paint=PaintInfo
+ }}
+ catch _:Err ->
+ io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]),
+ {stop, Err}
+ end.
+
+setup_graph_drawing(Panels) ->
+ IsWindows = element(1, os:type()) =:= win32,
+ IgnoreCB = {callback, fun(_,_) -> ok end},
+ Do = fun(Panel) ->
+ wxWindow:setBackgroundColour(Panel, ?wxWHITE),
+ wxPanel:connect(Panel, paint, [callback]),
+ IsWindows andalso
+ wxPanel:connect(Panel, erase_background, [IgnoreCB])
+ end,
+ _ = [Do(Panel) || Panel <- Panels],
UseGC = haveGC(),
Version28 = ?wxMAJOR_VERSION =:= 2 andalso ?wxMINOR_VERSION =:= 8,
{Font, SmallFont}
- = case os:type() of
- {unix, _} when UseGC, Version28 ->
+ = if UseGC, Version28 ->
%% Def font is really small when using Graphics contexts in 2.8
%% Hardcode it
F = wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD),
SF = wxFont:new(10, ?wxFONTFAMILY_DECORATIVE, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL),
{F, SF};
- _ ->
+ true ->
DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT),
DefSize = wxFont:getPointSize(DefFont),
DefFamily = wxFont:getFamily(DefFont),
- F = wxFont:new(DefSize, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD),
- SF = wxFont:new(DefSize-1, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL),
+ F = wxFont:new(DefSize-1, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD),
+ SF = wxFont:new(DefSize-2, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL),
{F, SF}
end,
BlackPen = wxPen:new({0,0,0}, [{width, 2}]),
- Pens = [wxPen:new(Col, [{width, 2}]) || Col <- tuple_to_list(colors())],
- process_flag(trap_exit, true),
- {Panel, #state{parent=Parent,
- panel =Panel,
- windows = {CPU, MEM, IO},
- usegc=UseGC,
- paint=#paint{font = Font,
- small = SmallFont,
- pen = ?wxGREY_PEN,
- pen2 = BlackPen,
- pens = list_to_tuple(Pens)
- }
- }}
- catch _:Err ->
- io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]),
- {stop, Err}
- end.
+ Pens = [wxPen:new(Col, [{width, 3}]) || Col <- tuple_to_list(colors())],
+ #paint{usegc = UseGC,
+ font = Font,
+ small = SmallFont,
+ pen = ?wxGREY_PEN,
+ pen2 = BlackPen,
+ pens = list_to_tuple(Pens)
+ }.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -139,21 +139,25 @@ handle_event(Event, _State) ->
%%%%%%%%%%
handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_,
#state{active=Active, offset=Offset, paint=Paint,
- windows=Windows, data=Data, usegc=UseGC}) ->
- %% PaintDC must be created in a callback to work on windows.
+ windows=Windows, data=Data}) ->
%% Sigh workaround bug on MacOSX (Id in paint event is always 0)
%% Panel = element(Id, Windows),
- Id = if Panel =:= element(?RQ_W, Windows) -> ?RQ_W;
- Panel =:= element(?MEM_W, Windows) -> ?MEM_W;
- Panel =:= element(?IO_W, Windows) -> ?IO_W
+ Id = if Panel =:= element(?RQ_W, Windows) -> runq;
+ Panel =:= element(?MEM_W, Windows) -> memory;
+ Panel =:= element(?IO_W, Windows) -> io
end,
- IsWindows = element(1, os:type()) =:= win32,
- DC = if IsWindows ->
+ refresh_panel(Panel, Id, Offset, Data, Active, Paint),
+ ok.
+
+refresh_panel(Panel, Id, Offset, Data, Active, #paint{usegc=UseGC} = Paint) ->
+ %% PaintDC must be created in a callback to work on windows.
+ IsWindows = element(1, os:type()) =:= win32,
+ DC = if IsWindows ->
%% Ugly hack to aviod flickering on windows, works on windows only
%% But the other platforms are doublebuffered by default
wx:typeCast(wxBufferedPaintDC:new(Panel), wxPaintDC);
- true ->
+ true ->
wxPaintDC:new(Panel)
end,
IsWindows andalso wxDC:clear(DC),
@@ -167,8 +171,9 @@ handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_,
io:format("Internal error ~p ~p~n",[Err, erlang:get_stacktrace()])
end,
UseGC andalso ?wxGC:destroy(GC),
- wxPaintDC:destroy(DC),
- ok.
+ wxPaintDC:destroy(DC).
+
+
%%%%%%%%%%
handle_call(Event, From, _State) ->
error({unhandled_call, Event, From}).
@@ -247,10 +252,10 @@ create_menus(Parent, _) ->
observer_wx:create_menus(Parent, MenuEntries).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-collect_data(?RQ_W, {N, Q}) ->
+collect_data(runq, {N, Q}) ->
case queue:to_list(Q) of
- [] -> {0, 0, []};
- [_] -> {0, 0, []};
+ [] -> {0, 0, [], []};
+ [_] -> {0, 0, [], []};
[{stats, _Ver, Init0, _IO, _Mem}|Data0] ->
Init = lists:sort(Init0),
[_|Data=[First|_]] = lists:foldl(fun({stats, _, T0, _, _}, [Prev|Acc]) ->
@@ -258,25 +263,46 @@ collect_data(?RQ_W, {N, Q}) ->
Delta = calc_delta(TN, Prev),
[TN, list_to_tuple(Delta)|Acc]
end, [Init], Data0),
- {N, lmax(Data), lists:reverse([First|Data])}
+ NoGraphs = tuple_size(First),
+ {N, lmax(Data), lists:reverse([First|Data]), lists:seq(1, NoGraphs)}
end;
-collect_data(?MEM_W, {N, Q}) ->
+collect_data(memory, {N, Q}) ->
MemT = mem_types(),
Data = [list_to_tuple([Value || {Type,Value} <- MemInfo,
lists:member(Type, MemT)])
|| {stats, _Ver, _RQ, _IO, MemInfo} <- queue:to_list(Q)],
- {N, lmax(Data), Data};
-collect_data(?IO_W, {N, Q}) ->
+ {N, lmax(Data), Data, MemT};
+collect_data(io, {N, Q}) ->
case queue:to_list(Q) of
- [] -> {0, 0, []};
- [_] -> {0, 0, []};
+ [] -> {0, 0, [], []};
+ [_] -> {0, 0, [], []};
[{stats, _Ver, _RQ, {{_,In0}, {_,Out0}}, _Mem}|Data0] ->
[_,_|Data=[First|_]] =
lists:foldl(fun({stats, _, _, {{_,In}, {_,Out}}, _}, [PIn,Pout|Acc]) ->
[In,Out,{In-PIn,Out-Pout}|Acc]
end, [In0,Out0], Data0),
- {N, lmax(Data), lists:reverse([First|Data])}
- end.
+ {N, lmax(Data), lists:reverse([First|Data]), [input, output]}
+ end;
+collect_data(alloc, {N, Q}) ->
+ List = queue:to_list(Q),
+ Data = [list_to_tuple([Carrier || {_Type,_Block,Carrier} <- MemInfo])
+ || MemInfo <- List],
+ Info = case List of %% Varies depending on erlang build config/platform
+ [MInfo|_] -> [Type || {Type, _, _} <- MInfo];
+ _ -> []
+ end,
+ {N, lmax(Data), Data, Info};
+
+collect_data(utilz, {N, Q}) ->
+ List = queue:to_list(Q),
+ Data = [list_to_tuple([round(100*Block/Carrier) || {_Type,Block,Carrier} <- MemInfo])
+ || MemInfo <- List],
+ Info = case List of %% Varies depending on erlang build config/platform
+ [MInfo|_] -> [Type || {Type, _, _} <- MInfo];
+ _ -> []
+ end,
+ {N, lmax(Data), Data, Info}.
+
mem_types() ->
[total, processes, atom, binary, code, ets].
@@ -299,14 +325,14 @@ draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens, small=Small}, Data, Active)
%% This can be optimized a lot by collecting data once
%% and draw to memory and then blit memory and only draw new entries in new memory
%% area. Hmm now rewritten to use ?wxGC I don't now if it is feasable.
- {Len, Max0, Hs} = collect_data(Id, Data),
- Max = calc_max(Max0),
- NoGraphs = try tuple_size(hd(Hs)) catch _:_ -> 0 end,
+ {Len, Max0, Hs, Info} = collect_data(Id, Data),
+ {Max,_,_} = MaxDisp = calc_max(Id, Max0),
Size = wxWindow:getClientSize(Panel),
- {X0,Y0,WS,HS} = draw_borders(Id, NoGraphs, DC, Size, Max, Paint),
+ {X0,Y0,WS,HS, DrawBs} = draw_borders(Id, Info, DC, Size, MaxDisp, Paint),
Last = 60*WS+X0-1,
Start = max(61-Len, 0)*WS+X0 - Offset*WS,
Samples = length(Hs),
+ NoGraphs = try tuple_size(hd(Hs)) catch _:_ -> 0 end,
case Active andalso Samples > 1 andalso NoGraphs > 0 of
true ->
Draw = fun(N) ->
@@ -315,14 +341,16 @@ draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens, small=Small}, Data, Active)
strokeLines(DC, Lines),
N+1
end,
- [Draw(I) || I <- lists:seq(NoGraphs, 1, -1)];
+ [Draw(I) || I <- lists:seq(NoGraphs, 1, -1)],
+ DrawBs();
false ->
- Info = case Active andalso Samples =< 1 of
- true -> "Waiting on data";
+ DrawBs(),
+ Text = case Active andalso Samples =< 1 of
+ true -> "Waiting for data";
false -> "Information not available"
end,
setFont(DC, Small, {0,0,0}),
- drawText(DC, Info, X0 + 100, element(2,Size) div 2)
+ drawText(DC, Text, X0 + 100, element(2,Size) div 2)
end,
ok.
@@ -397,9 +425,8 @@ spline_tan(Y0, Y1, Y2, Y3) ->
-define(BW, 5).
-define(BH, 5).
-draw_borders(Type, NoGraphs, DC, {W,H}, Max,
+draw_borders(Type, Info, DC, {W,H}, {Max, Unit, MaxUnit},
#paint{pen=Pen, pen2=Pen2, font=Font, small=Small}) ->
- {Unit, MaxUnit} = bytes(Type, Max),
Str1 = observer_lib:to_str(MaxUnit),
Str2 = observer_lib:to_str(MaxUnit div 2),
Str3 = observer_lib:to_str(0),
@@ -410,10 +437,10 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max,
GraphX0 = ?BW+TW+?BW,
GraphX1 = W-?BW*4,
- TopTextX = ?BW+TW+?BW,
- MaxTextY = ?BH+TH+?BH,
+ TopTextX = ?BW*3+TW,
+ MaxTextY = TH+?BH,
BottomTextY = H-?BH-TH,
- SecondsY = BottomTextY - ?BH - TH,
+ SecondsY = BottomTextY - TH,
GraphY0 = MaxTextY + (TH / 2),
GraphY1 = SecondsY - ?BH,
GraphW = GraphX1-GraphX0-1,
@@ -447,17 +474,7 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max,
strokeLine(DC, GraphX0-3, GraphY50, GraphX1, GraphY50),
strokeLine(DC, GraphX0-3, GraphY75, GraphX1, GraphY75),
- setPen(DC, Pen2),
- strokeLines(DC, [{GraphX0, GraphY0-1}, {GraphX0, GraphY1+1},
- {GraphX1, GraphY1+1}, {GraphX1, GraphY0-1},
- {GraphX0, GraphY0-1}]),
-
setFont(DC, Font, {0,0,0}),
- case Type of
- ?RQ_W -> drawText(DC, "Scheduler Utilization (%) ", TopTextX,?BH);
- ?MEM_W -> drawText(DC, "Memory Usage " ++ Unit, TopTextX,?BH);
- ?IO_W -> drawText(DC, "IO Usage " ++ Unit, TopTextX,?BH)
- end,
Text = fun(X,Y, Str, PenId) ->
if PenId == 0 ->
@@ -468,32 +485,65 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max,
end,
drawText(DC, Str, X, Y),
{StrW, _} = getTextExtent(DC, Str),
- StrW + X + SpaceW
+ StrW + X + ?BW*2
end,
+
case Type of
- ?RQ_W ->
- TN0 = Text(?BW, BottomTextY, "Scheduler: ", 0),
+ runq ->
+ drawText(DC, "Scheduler Utilization (%) ", TopTextX, ?BH),
+ TN0 = Text(TopTextX, BottomTextY, "Scheduler: ", 0),
lists:foldl(fun(Id, Pos0) ->
Text(Pos0, BottomTextY, integer_to_list(Id), Id)
- end, TN0, lists:seq(1, NoGraphs));
- ?MEM_W ->
+ end, TN0, Info);
+ memory ->
+ drawText(DC, "Memory Usage " ++ Unit, TopTextX,?BH),
+ lists:foldl(fun(MType, {PenId, Pos0}) ->
+ Str = to_string(MType),
+ Pos = Text(Pos0, BottomTextY, Str, PenId),
+ {PenId+1, Pos}
+ end, {1, TopTextX}, Info);
+ io ->
+ drawText(DC, "IO Usage " ++ Unit, TopTextX,?BH),
+ lists:foldl(fun(MType, {PenId, Pos0}) ->
+ Str = to_string(MType),
+ Pos = Text(Pos0, BottomTextY, Str, PenId),
+ {PenId+1, Pos}
+ end, {1, TopTextX}, Info);
+ alloc ->
+ drawText(DC, "Carrier Size " ++ Unit, TopTextX,?BH);
+ utilz ->
+ drawText(DC, "Carrier Utilization (%)" ++ Unit, TopTextX,?BH),
lists:foldl(fun(MType, {PenId, Pos0}) ->
- Str = uppercase(atom_to_list(MType)),
+ Str = to_string(MType),
Pos = Text(Pos0, BottomTextY, Str, PenId),
{PenId+1, Pos}
- end, {1, ?BW}, mem_types());
- ?IO_W ->
- TN0 = Text(?BW, BottomTextY, "Input", 1),
- Text(TN0, BottomTextY, "Output", 2)
+ end, {1, TopTextX}, Info)
end,
- {GraphX0+1, GraphY1, ScaleW, ScaleH}.
+ DrawBorder = fun() ->
+ setPen(DC, Pen2),
+ strokeLines(DC, [{GraphX0, GraphY0-1}, {GraphX0, GraphY1+1},
+ {GraphX1, GraphY1+1}, {GraphX1, GraphY0-1},
+ {GraphX0, GraphY0-1}])
+ end,
+ {GraphX0+1, GraphY1, ScaleW, ScaleH, DrawBorder}.
+
+to_string(Atom) ->
+ Name = atom_to_list(Atom),
+ case lists:reverse(Name) of
+ "colla_" ++ Rev ->
+ uppercase(lists:reverse(Rev));
+ _ ->
+ uppercase(Name)
+ end.
uppercase([C|Rest]) ->
[C-$a+$A|Rest].
-calc_max(Max) when Max < 10 -> 10;
-calc_max(Max) -> calc_max1(Max).
+calc_max(Type, Max) ->
+ bytes(Type, Max).
+calc_max1(Max) when Max < 10 ->
+ 10;
calc_max1(Max) ->
case Max div 10 of
X when X < 10 ->
@@ -506,23 +556,36 @@ calc_max1(Max) ->
10*calc_max1(X)
end.
-bytes(?RQ_W, Val) -> {"", Val};
+bytes(runq, Val) ->
+ Upper = calc_max1(Val),
+ {Upper, "", Upper};
+bytes(utilz, Val) ->
+ Upper = calc_max1(Val),
+ {Upper, "", Upper};
bytes(_, B) ->
KB = B div 1024,
MB = KB div 1024,
GB = MB div 1024,
if
- GB > 10 -> {"(GB)", GB};
- MB > 10 -> {"(MB)", MB};
- KB > 0 -> {"(KB)", KB};
- true -> {"(B)", B}
+ GB > 10 ->
+ Upper = calc_max1(GB),
+ {Upper*1024*1024*1024, "(GB)", Upper};
+ MB > 10 ->
+ Upper = calc_max1(MB),
+ {Upper*1024*1024, "(MB)", Upper};
+ KB > 0 ->
+ Upper = calc_max1(KB),
+ {Upper*1024, "(KB)", Upper};
+ true ->
+ Upper = calc_max1(B),
+ {Upper, "(B)", Upper}
end.
colors() ->
- {{200, 50, 50}, {50, 200, 50}, {50, 50, 200},
- {255, 110, 0}, {50, 200, 200}, {200, 50, 200},
- {240, 200, 80}, {140, 2, 140},
- {100, 200, 240}, {100, 240, 100}
+ {{240, 100, 100}, {100, 240, 100}, {100, 100, 240},
+ {220, 220, 80}, {100, 240, 240}, {240, 100, 240},
+ {100, 25, 25}, {25, 100, 25}, {25, 25, 100},
+ {120, 120, 0}, {25, 100, 100}, {100, 50, 100}
}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index 0be8c18893..026693ff56 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -578,7 +578,7 @@ get_row(From, Row, pid, Info) ->
end,
From ! {self(), Pid};
get_row(From, Row, Col, Info) ->
- Data = case Row > array:size(Info) of
+ Data = case Row >= array:size(Info) of
true ->
"";
false ->
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index 8e8a37fc93..d724cd9e96 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -43,6 +43,8 @@
-record(worker, {panel, callback}).
+-record(io, {rdata=""}).
+
start(Process, ParentFrame, Parent) ->
wx_object:start_link(?MODULE, [Process, ParentFrame, Parent], []).
@@ -69,6 +71,10 @@ init([Pid, ParentFrame, Parent]) ->
DictPage = init_panel(Notebook, "Dictionary", [Pid,Table], fun init_dict_page/3),
StackPage = init_panel(Notebook, "Stack Trace", [Pid], fun init_stack_page/2),
StatePage = init_panel(Notebook, "State", [Pid,Table], fun init_state_page/3),
+ Ps = case gen_server:call(observer, log_status) of
+ true -> [init_panel(Notebook, "Log", [Pid,Table], fun init_log_page/3)];
+ false -> []
+ end,
wxFrame:connect(Frame, close_window),
wxMenu:connect(Frame, command_menu_selected),
@@ -78,7 +84,7 @@ init([Pid, ParentFrame, Parent]) ->
pid=Pid,
frame=Frame,
notebook=Notebook,
- pages=[ProcessPage,MessagePage,DictPage,StackPage,StatePage],
+ pages=[ProcessPage,MessagePage,DictPage,StackPage,StatePage|Ps],
expand_table=Table
}}
catch error:{badrpc, _} ->
@@ -144,7 +150,7 @@ handle_event(#wx{event=#wxHtmlLink{linkInfo=#wxHtmlLinkInfo{href=Href}}},
Opened =
case lists:keyfind(Id,1,Opened0) of
false ->
- Win = cdv_detail_wx:start_link(Id,Frame,Callback),
+ Win = cdv_detail_wx:start_link(Id,[],Frame,Callback),
[{Id,Win}|Opened0];
{_,Win} ->
wxFrame:raise(Win),
@@ -327,6 +333,26 @@ fetch_state_info2(Pid, M) ->
{badrpc,{'EXIT',{timeout, _}}} -> []
end.
+init_log_page(Parent, Pid, Table) ->
+ Win = observer_lib:html_window(Parent),
+ Update = fun() ->
+ Fd = spawn_link(fun() -> io_server() end),
+ rpc:call(node(Pid), rb, rescan, [[{start_log, Fd}]]),
+ rpc:call(node(Pid), rb, grep, [local_pid_str(Pid)]),
+ Logs = io_get_data(Fd),
+ %% Replace remote local pid notation to global notation
+ Pref = global_pid_node_pref(Pid),
+ ExpPid = re:replace(Logs,"<0\.","<" ++ Pref ++ ".",[global, {return, list}]),
+ %% Try to keep same look by removing blanks at right of rewritten PID
+ NbBlanks = length(Pref) - 1,
+ Re = "(<" ++ Pref ++ "\.[^>]{1,}>)[ ]{"++ integer_to_list(NbBlanks) ++ "}",
+ Look = re:replace(ExpPid, Re, "\\1", [global, {return, list}]),
+ Html = observer_html_lib:expandable_term("SaslLog", Look, Table),
+ wxHtmlWindow:setPage(Win, Html)
+ end,
+ Update(),
+ {Win, Update}.
+
create_menus(MenuBar) ->
Menus = [{"File", [#create_menu{id=?wxID_CLOSE, text="Close"}]},
{"View", [#create_menu{id=?REFRESH, text="Refresh\tCtrl-R"}]}],
@@ -409,3 +435,51 @@ filter_monitor_info() ->
Ms = proplists:get_value(monitors, Data),
[Pid || {process, Pid} <- Ms]
end.
+
+local_pid_str(Pid) ->
+ %% observer can observe remote nodes
+ %% There is no function to get the local
+ %% pid from the remote pid ...
+ %% So grep will fail to find remote pid in remote local log.
+ %% i.e. <4589.42.1> will not be found, but <0.42.1> will
+ %% Let's replace first integer by zero
+ "<0" ++ re:replace(pid_to_list(Pid),"\<([0-9]{1,})","",[{return, list}]).
+
+global_pid_node_pref(Pid) ->
+ %% Global PID node prefix : X of <X.Y.Z>
+ string:strip(string:sub_word(pid_to_list(Pid),1,$.),left,$<).
+
+
+io_get_data(Pid) ->
+ Pid ! {self(), get_data_and_close},
+ receive
+ {Pid, data, Data} -> lists:flatten(Data)
+ end.
+
+io_server() ->
+ io_server(#io{}).
+
+io_server(State) ->
+ receive
+ {io_request, From, ReplyAs, Request} ->
+ {_, Reply, NewState} = io_request(Request,State),
+ From ! {io_reply, ReplyAs, Reply},
+ io_server(NewState);
+ {Pid, get_data_and_close} ->
+ Pid ! {self(), data, lists:reverse(State#io.rdata)},
+ normal;
+ _Unknown ->
+ io_server(State)
+ end.
+
+io_request({put_chars, _Encoding, Chars}, State = #io{rdata=Data}) ->
+ {ok, ok, State#io{rdata=[Chars|Data]}};
+io_request({put_chars, Encoding, Module, Function, Args}, State) ->
+ try
+ io_request({put_chars, Encoding, apply(Module, Function, Args)}, State)
+ catch _:_ ->
+ {error, {error, Function}, State}
+ end;
+io_request(_Req, State) ->
+ %% io:format("~p: Unknown req: ~p ~n",[?LINE, _Req]),
+ {ok, {error, request}, State}.
diff --git a/lib/observer/src/observer_sys_wx.erl b/lib/observer/src/observer_sys_wx.erl
index f989f9cf97..ea89590e84 100644
--- a/lib/observer/src/observer_sys_wx.erl
+++ b/lib/observer/src/observer_sys_wx.erl
@@ -37,7 +37,6 @@
parent_notebook,
panel, sizer,
menubar,
- alloc,
fields,
timer}).
@@ -48,7 +47,6 @@ start_link(Notebook, Parent) ->
init([Notebook, Parent]) ->
SysInfo = observer_backend:sys_info(),
- AllocInfo = proplists:get_value(alloc_info, SysInfo, []),
{Info, Stat} = info_fields(),
Panel = wxPanel:new(Notebook),
Sizer = wxBoxSizer:new(?wxVERTICAL),
@@ -60,16 +58,13 @@ init([Notebook, Parent]) ->
wxSizer:add(TopSizer, FPanel0, [{flag, ?wxEXPAND}, {proportion, 1}]),
wxSizer:add(TopSizer, FPanel1, [{flag, ?wxEXPAND}, {proportion, 1}]),
BorderFlags = ?wxLEFT bor ?wxRIGHT,
- {MemPanel, MemoryInfo} = create_mem_info(Panel, AllocInfo),
wxSizer:add(Sizer, TopSizer, [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP},
{proportion, 0}, {border, 5}]),
- wxSizer:add(Sizer, MemPanel, [{flag, ?wxEXPAND bor BorderFlags bor ?wxBOTTOM},
- {proportion, 1}, {border, 5}]),
wxPanel:setSizer(Panel, Sizer),
Timer = observer_lib:start_timer(10),
{Panel, #sys_wx_state{parent=Parent,
parent_notebook=Notebook,
- panel=Panel, sizer=Sizer, alloc=MemoryInfo,
+ panel=Panel, sizer=Sizer,
timer=Timer, fields=Fields0 ++ Fields1}}.
create_sys_menu(Parent) ->
@@ -77,91 +72,13 @@ create_sys_menu(Parent) ->
#create_menu{id = ?ID_REFRESH_INTERVAL, text = "Refresh interval"}]},
observer_wx:create_menus(Parent, [View]).
-update_syspage(#sys_wx_state{node = Node, fields=Fields, sizer=Sizer, alloc=AllocCtrl}) ->
+update_syspage(#sys_wx_state{node = Node, fields=Fields, sizer=Sizer}) ->
SysInfo = observer_wx:try_rpc(Node, observer_backend, sys_info, []),
- AllocInfo = proplists:get_value(alloc_info, SysInfo, []),
{Info, Stat} = info_fields(),
observer_lib:update_info(Fields, observer_lib:fill_info(Info, SysInfo) ++
observer_lib:fill_info(Stat, SysInfo)),
- update_alloc(AllocCtrl, AllocInfo),
wxSizer:layout(Sizer).
-create_mem_info(Parent, Fields) ->
- Panel = wxPanel:new(Parent),
- wxWindow:setBackgroundColour(Panel, {255,255,255}),
- Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES bor ?wxLC_VRULES,
- Grid = wxListCtrl:new(Panel, [{style, Style}]),
- Li = wxListItem:new(),
- AddListEntry = fun({Name, Align, DefSize}, Col) ->
- wxListItem:setText(Li, Name),
- wxListItem:setAlign(Li, Align),
- wxListCtrl:insertColumn(Grid, Col, Li),
- wxListCtrl:setColumnWidth(Grid, Col, DefSize),
- Col + 1
- end,
- ListItems = [{"Allocator Type", ?wxLIST_FORMAT_LEFT, 200},
- {"Block size (kB)", ?wxLIST_FORMAT_RIGHT, 150},
- {"Carrier size (kB)",?wxLIST_FORMAT_RIGHT, 150}],
- lists:foldl(AddListEntry, 0, ListItems),
- wxListItem:destroy(Li),
- update_alloc(Grid, Fields),
-
- Sizer = wxBoxSizer:new(?wxVERTICAL),
- wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxLEFT bor ?wxRIGHT},
- {border, 5}, {proportion, 1}]),
- wxWindow:setSizerAndFit(Panel, Sizer),
- {Panel, Grid}.
-
-update_alloc(Grid, AllocInfo) ->
- Fields = alloc_info(AllocInfo, [], 0, 0, true),
- wxListCtrl:deleteAllItems(Grid),
- Update = fun({Name, BS, CS}, Row) ->
- wxListCtrl:insertItem(Grid, Row, ""),
- wxListCtrl:setItem(Grid, Row, 0, observer_lib:to_str(Name)),
- wxListCtrl:setItem(Grid, Row, 1, observer_lib:to_str(BS div 1024)),
- wxListCtrl:setItem(Grid, Row, 2, observer_lib:to_str(CS div 1024)),
- Row + 1
- end,
- lists:foldl(Update, 0, Fields),
- Fields.
-
-alloc_info([{Type,Instances}|Allocators],TypeAcc,TotalBS,TotalCS,IncludeTotal) ->
- {BS,CS,NewTotalBS,NewTotalCS,NewIncludeTotal} =
- sum_alloc_instances(Instances,0,0,TotalBS,TotalCS),
- alloc_info(Allocators,[{Type,BS,CS}|TypeAcc],NewTotalBS,NewTotalCS,
- IncludeTotal andalso NewIncludeTotal);
-alloc_info([],TypeAcc,TotalBS,TotalCS,IncludeTotal) ->
- Types = [X || X={_,BS,CS} <- TypeAcc, (BS>0 orelse CS>0)],
- case IncludeTotal of
- true ->
- [{total,TotalBS,TotalCS} | lists:reverse(Types)];
- false ->
- lists:reverse(Types)
- end.
-
-sum_alloc_instances(false,BS,CS,TotalBS,TotalCS) ->
- {BS,CS,TotalBS,TotalCS,false};
-sum_alloc_instances([{_,_,Data}|Instances],BS,CS,TotalBS,TotalCS) ->
- {NewBS,NewCS,NewTotalBS,NewTotalCS} =
- sum_alloc_one_instance(Data,BS,CS,TotalBS,TotalCS),
- sum_alloc_instances(Instances,NewBS,NewCS,NewTotalBS,NewTotalCS);
-sum_alloc_instances([],BS,CS,TotalBS,TotalCS) ->
- {BS,CS,TotalBS,TotalCS,true}.
-
-sum_alloc_one_instance([{sbmbcs,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}|
- Rest],OldBS,OldCS,TotalBS,TotalCS) ->
- sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS,TotalCS);
-sum_alloc_one_instance([{_,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}|
- Rest],OldBS,OldCS,TotalBS,TotalCS) ->
- sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS+BS,TotalCS+CS);
-sum_alloc_one_instance([{_,[{blocks_size,BS},{carriers_size,CS}]}|
- Rest],OldBS,OldCS,TotalBS,TotalCS) ->
- sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS+BS,TotalCS+CS);
-sum_alloc_one_instance([_|Rest],BS,CS,TotalBS,TotalCS) ->
- sum_alloc_one_instance(Rest,BS,CS,TotalBS,TotalCS);
-sum_alloc_one_instance([],BS,CS,TotalBS,TotalCS) ->
- {BS,CS,TotalBS,TotalCS}.
-
info_fields() ->
Info = [{"System and Architecture",
[{"System Version", otp_release},
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index da4cb8e041..acfa6a2a99 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -176,10 +176,16 @@ handle_call(Event, From, _State) ->
handle_cast(Event, _State) ->
error({unhandled_cast, Event}).
-handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt}) ->
- Tables = get_tables(Node, Opt),
- Tabs = update_grid(Grid, Opt, Tables),
- {noreply, State#state{tabs=Tabs}};
+handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt,
+ tabs=OldTabs}) ->
+ case get_tables(Node, Opt) of
+ OldTabs ->
+ %% no change
+ {noreply, State};
+ Tables ->
+ Tabs = update_grid(Grid, Opt, Tables),
+ {noreply, State#state{tabs=Tabs}}
+ end;
handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt,
timer=Timer0}) ->
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 15df804975..cf602569aa 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -37,11 +37,13 @@
-define(ID_CONNECT, 2).
-define(ID_NOTEBOOK, 3).
-define(ID_CDV, 4).
+-define(ID_LOGVIEW, 5).
-define(FIRST_NODES_MENU_ID, 1000).
-define(LAST_NODES_MENU_ID, 2000).
-define(TRACE_STR, "Trace Overview").
+-define(ALLOC_STR, "Memory Allocators").
%% Records
-record(state,
@@ -57,10 +59,12 @@
trace_panel,
app_panel,
perf_panel,
+ allc_panel,
active_tab,
node,
nodes,
- prev_node=""
+ prev_node="",
+ log = false
}).
start() ->
@@ -147,6 +151,10 @@ setup(#state{frame = Frame} = State) ->
PerfPanel = observer_perf_wx:start_link(Notebook, self()),
wxNotebook:addPage(Notebook, PerfPanel, "Load Charts", []),
+ %% Memory Allocator Viewer Panel
+ AllcPanel = observer_alloc_wx:start_link(Notebook, self()),
+ wxNotebook:addPage(Notebook, AllcPanel, ?ALLOC_STR, []),
+
%% App Viewer Panel
AppPanel = observer_app_wx:start_link(Notebook, self()),
wxNotebook:addPage(Notebook, AppPanel, "Applications", []),
@@ -182,6 +190,7 @@ setup(#state{frame = Frame} = State) ->
trace_panel = TracePanel,
app_panel = AppPanel,
perf_panel = PerfPanel,
+ allc_panel = AllcPanel,
active_tab = SysPid,
node = node(),
nodes = Nodes
@@ -215,14 +224,17 @@ handle_event(#wx{event=#wxNotebook{type=command_notebook_page_changing}},
{noreply, State#state{active_tab=Pid}}
end;
-handle_event(#wx{event = #wxClose{}}, State) ->
- {stop, normal, State};
-
handle_event(#wx{id = ?ID_CDV, event = #wxCommand{type = command_menu_selected}}, State) ->
spawn(crashdump_viewer, start, []),
{noreply, State};
-handle_event(#wx{id = ?wxID_EXIT, event = #wxCommand{type = command_menu_selected}}, State) ->
+handle_event(#wx{event = #wxClose{}}, #state{log=LogOn} = State) ->
+ LogOn andalso rpc:block_call(State#state.node, rb, stop, []),
+ {stop, normal, State};
+
+handle_event(#wx{id = ?wxID_EXIT, event = #wxCommand{type = command_menu_selected}},
+ #state{log=LogOn} = State) ->
+ LogOn andalso rpc:block_call(State#state.node, rb, stop, []),
{stop, normal, State};
handle_event(#wx{id = ?wxID_HELP, event = #wxCommand{type = command_menu_selected}}, State) ->
@@ -300,12 +312,42 @@ handle_event(#wx{id = ?ID_PING, event = #wxCommand{type = command_menu_selected}
end,
{noreply, UpdState};
-handle_event(#wx{id = Id, event = #wxCommand{type = command_menu_selected}}, State)
- when Id > ?FIRST_NODES_MENU_ID, Id < ?LAST_NODES_MENU_ID ->
+handle_event(#wx{id = ?ID_LOGVIEW, event = #wxCommand{type = command_menu_selected}},
+ #state{frame = Frame, log = PrevLog, node = Node} = State) ->
+ try
+ ok = ensure_sasl_started(Node),
+ ok = ensure_mf_h_handler_used(Node),
+ ok = ensure_rb_mode(Node, PrevLog),
+ case PrevLog of
+ false ->
+ rpc:block_call(Node, rb, start, []),
+ set_status("Observer - " ++ atom_to_list(Node) ++ " (rb_server started)"),
+ {noreply, State#state{log=true}};
+ true ->
+ rpc:block_call(Node, rb, stop, []),
+ set_status("Observer - " ++ atom_to_list(Node) ++ " (rb_server stopped)"),
+ {noreply, State#state{log=false}}
+ end
+ catch
+ throw:Reason ->
+ create_txt_dialog(Frame, Reason, "Log view status", ?wxICON_ERROR),
+ {noreply, State}
+ end;
- Node = lists:nth(Id - ?FIRST_NODES_MENU_ID, State#state.nodes),
- UpdState = change_node_view(Node, State),
- {noreply, UpdState};
+handle_event(#wx{id = Id, event = #wxCommand{type = command_menu_selected}},
+ #state{nodes= Ns , node = PrevNode, log = PrevLog} = State)
+ when Id > ?FIRST_NODES_MENU_ID, Id < ?LAST_NODES_MENU_ID ->
+ Node = lists:nth(Id - ?FIRST_NODES_MENU_ID, Ns),
+ %% Close rb_server only if another node than current one selected
+ LState = case PrevLog of
+ true -> case Node == PrevNode of
+ false -> rpc:block_call(PrevNode, rb, stop, []),
+ State#state{log=false} ;
+ true -> State
+ end;
+ false -> State
+ end,
+ {noreply, change_node_view(Node, LState)};
handle_event(Event, State) ->
Pid = get_active_pid(State),
@@ -340,6 +382,9 @@ handle_call(stop, _, State = #state{frame = Frame}) ->
wxFrame:destroy(Frame),
{stop, normal, ok, State};
+handle_call(log_status, _From, State) ->
+ {reply, State#state.log, State};
+
handle_call(_Msg, _From, State) ->
{reply, ok, State}.
@@ -467,7 +512,7 @@ check_page_title(Notebook) ->
get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys,
tv_panel=Tv, trace_panel=Trace, app_panel=App,
- perf_panel=Perf
+ perf_panel=Perf, allc_panel=Alloc
}) ->
Panel = case check_page_title(Notebook) of
"Processes" -> Pro;
@@ -475,13 +520,14 @@ get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys,
"Table Viewer" -> Tv;
?TRACE_STR -> Trace;
"Load Charts" -> Perf;
- "Applications" -> App
+ "Applications" -> App;
+ ?ALLOC_STR -> Alloc
end,
wx_object:get_pid(Panel).
pid2panel(Pid, #state{pro_panel=Pro, sys_panel=Sys,
tv_panel=Tv, trace_panel=Trace, app_panel=App,
- perf_panel=Perf}) ->
+ perf_panel=Perf, allc_panel=Alloc}) ->
case Pid of
Pro -> "Processes";
Sys -> "System";
@@ -489,6 +535,7 @@ pid2panel(Pid, #state{pro_panel=Pro, sys_panel=Sys,
Trace -> ?TRACE_STR;
Perf -> "Load Charts";
App -> "Applications";
+ Alloc -> ?ALLOC_STR;
_ -> "unknown"
end.
@@ -568,17 +615,19 @@ default_menus(NodesMenuItems) ->
false -> {"Nodes", NodesMenuItems ++
[#create_menu{id = ?ID_CONNECT, text = "Enable distribution"}]}
end,
+ LogMenu = {"Log", [#create_menu{id = ?ID_LOGVIEW, text = "Toggle log view"}]},
case os:type() =:= {unix, darwin} of
false ->
FileMenu = {"File", [CDV, Quit]},
HelpMenu = {"Help", [About,Help]},
- [FileMenu, NodeMenu, HelpMenu];
+ [FileMenu, NodeMenu, LogMenu, HelpMenu];
true ->
%% On Mac quit and about will be moved to the "default' place
%% automagicly, so just add them to a menu that always exist.
%% But not to the help menu for some reason
+
{Tag, Menus} = FileMenu,
- [{Tag, Menus ++ [About]}, NodeMenu, {"&Help", [Help]}]
+ [{Tag, Menus ++ [Quit,About]}, NodeMenu, LogMenu, {"&Help", [Help]}]
end.
clean_menus(Menus, MenuBar) ->
@@ -657,3 +706,59 @@ update_node_list(State = #state{menubar=MenuBar}) ->
end,
observer_lib:create_menu_item(Dist, NodeMenu, Index),
State#state{nodes = Nodes}.
+
+ensure_sasl_started(Node) ->
+ %% is sasl started ?
+ Apps = rpc:block_call(Node, application, which_applications, []),
+ case lists:keyfind(sasl, 1, Apps) of
+ false -> throw("Error: sasl application not started."),
+ error;
+ {sasl, _, _} -> ok
+ end.
+
+ensure_mf_h_handler_used(Node) ->
+ %% is log_mf_h used ?
+ Handlers = rpc:block_call(Node, gen_event, which_handlers, [error_logger]),
+ case lists:any(fun(L)-> L == log_mf_h end, Handlers) of
+ false -> throw("Error: log_mf_h handler not used in sasl."),
+ error;
+ true -> ok
+ end.
+
+ensure_rb_mode(Node, PrevLog) ->
+ ok = ensure_rb_module_loaded(Node),
+ ok = is_rb_compatible(Node),
+ ok = is_rb_server_running(Node, PrevLog),
+ ok.
+
+
+ensure_rb_module_loaded(Node) ->
+ %% Need to ensure that module is loaded in order to detect exported
+ %% functions on interactive nodes
+ case rpc:block_call(Node, code, ensure_loaded, [rb]) of
+ {badrpc, Reason} ->
+ throw("Error: badrpc - " ++ io_lib:format("~tp",[Reason]));
+ {error, Reason} ->
+ throw("Error: rb module load error - " ++ io_lib:format("~tp",[Reason]));
+ {module,rb} ->
+ ok
+ end.
+
+is_rb_compatible(Node) ->
+ %% Simply test that rb:log_list/0 is exported
+ case rpc:block_call(Node, erlang, function_exported, [rb, log_list, 0]) of
+ false -> throw("Error: Node's Erlang release must be at least R16B02.");
+ true -> ok
+ end.
+
+is_rb_server_running(Node, LogState) ->
+ %% If already started, somebody else may use it.
+ %% We can not use it too, as far log file would be overriden. Not fair.
+ case rpc:block_call(Node, erlang, whereis, [rb_server]) of
+ Pid when is_pid(Pid), (LogState == false) ->
+ throw("Error: rb_server is already started and maybe used by someone.");
+ Pid when is_pid(Pid) ->
+ ok;
+ undefined ->
+ ok
+ end.
diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl
index 61fd6d1787..a2db40aa2f 100644
--- a/lib/observer/src/ttb.erl
+++ b/lib/observer/src/ttb.erl
@@ -849,7 +849,7 @@ get_nodes() ->
receive {?MODULE,Nodes} -> Nodes end.
ts() ->
- {{Y,M,D},{H,Min,S}} = calendar:now_to_local_time(now()),
+ {{Y,M,D},{H,Min,S}} = calendar:now_to_local_time(erlang:timestamp()),
io_lib:format("-~4.4.0w~2.2.0w~2.2.0w-~2.2.0w~2.2.0w~2.2.0w",
[Y,M,D,H,Min,S]).