aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/observer/src/crashdump_viewer.erl269
-rw-r--r--lib/observer/src/crashdump_viewer.hrl5
-rw-r--r--lib/observer/src/crashdump_viewer_html.erl84
-rw-r--r--lib/observer/src/observer_lib.erl7
-rw-r--r--lib/observer/src/observer_sys_wx.erl94
-rw-r--r--lib/runtime_tools/src/observer_backend.erl24
6 files changed, 417 insertions, 66 deletions
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index 64a8457d16..e7d71c581e 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2013. 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
@@ -41,6 +41,7 @@
%% Process state
%% -------------
%% file: The name of the crashdump currently viewed.
+%% dump_vsn: The version number of the crashdump
%% procs_summary: Process summary represented by a list of
%% #proc records. This is used for efficiency reasons when sorting the
%% process summary table instead of reading all processes from the
@@ -127,6 +128,8 @@
% timers, funs...
% Must be equal to
% ?max_sort_process_num!
+-define(not_available,"N/A").
+
%% All possible tags - use macros in order to avoid misspelling in the code
-define(allocated_areas,allocated_areas).
@@ -161,7 +164,7 @@
-define(visible_node,visible_node).
--record(state,{file,procs_summary,sorted,shared_heap=false,
+-record(state,{file,dump_vsn,procs_summary,sorted,shared_heap=false,
wordsize=4,num_atoms="unknown",binaries,bg_status}).
%%%-----------------------------------------------------------------
@@ -499,6 +502,7 @@ handle_call(filename_frame,_From,State=#state{file=File}) ->
{reply,Reply,State};
handle_call(initial_info_frame,_From,State=#state{file=File}) ->
GenInfo = general_info(File),
+ [{DumpVsn,_}] = lookup_index(?erl_crash_dump),
NumAtoms = GenInfo#general_info.num_atoms,
{WS,SH} = parse_vsn_str(GenInfo#general_info.system_vsn,4,false),
NumProcs = list_to_integer(GenInfo#general_info.num_procs),
@@ -506,7 +510,9 @@ handle_call(initial_info_frame,_From,State=#state{file=File}) ->
if NumProcs > ?max_sort_process_num -> too_many;
true -> State#state.procs_summary
end,
- NewState = State#state{shared_heap=SH,
+ NewState = State#state{dump_vsn=[list_to_integer(L) ||
+ L<-string:tokens(DumpVsn,".")],
+ shared_heap=SH,
wordsize=WS,
num_atoms=NumAtoms,
procs_summary=ProcsSummary},
@@ -577,7 +583,7 @@ handle_call({sort_procs,SessionId,Input}, _From, State) ->
handle_call({proc_details,Input},_From,State=#state{file=File,shared_heap=SH}) ->
{ok,Pid} = get_value("pid",httpd:parse_query(Input)),
Reply =
- case get_proc_details(File,Pid) of
+ case get_proc_details(File,Pid,State#state.dump_vsn) of
{ok,Proc} ->
TW = truncated_warning([{?proc,Pid}]),
crashdump_viewer_html:proc_details(Pid,Proc,TW,SH);
@@ -1298,7 +1304,6 @@ find_truncated_proc({Tag,Pid}) ->
is_proc_tag(Tag) when Tag==?proc;
Tag==?proc_dictionary;
Tag==?proc_messages;
- Tag==?proc_dictionary;
Tag==?debug_proc_dictionary;
Tag==?proc_stack;
Tag==?proc_heap ->
@@ -1451,7 +1456,8 @@ count() ->
%% avoid really big data in the server state.
procs_summary(SessionId,TW,_,State=#state{procs_summary=too_many}) ->
chunk_page(SessionId,State#state.file,TW,?proc,processes,
- {no_sort,State#state.shared_heap},procs_summary_parsefun()),
+ {no_sort,State#state.shared_heap,State#state.dump_vsn},
+ procs_summary_parsefun()),
State;
procs_summary(SessionId,TW,SortOn,State) ->
ProcsSummary =
@@ -1465,10 +1471,12 @@ procs_summary(SessionId,TW,SortOn,State) ->
PS ->
PS
end,
- {SortedPS,NewSorted} = do_sort_procs(SortOn,ProcsSummary,State#state.sorted),
+ {SortedPS,NewSorted} = do_sort_procs(SortOn,ProcsSummary,State),
HtmlInfo =
crashdump_viewer_html:chunk_page(processes,SessionId,TW,
- {SortOn,State#state.shared_heap},
+ {SortOn,
+ State#state.shared_heap,
+ State#state.dump_vsn},
SortedPS),
crashdump_viewer_html:chunk(SessionId,done,HtmlInfo),
State#state{procs_summary=ProcsSummary,sorted=NewSorted}.
@@ -1480,15 +1488,14 @@ procs_summary_parsefun() ->
%%-----------------------------------------------------------------
%% Page with one process
-get_proc_details(File,Pid) ->
- [{DumpVsn,_}] = lookup_index(?erl_crash_dump),
+get_proc_details(File,Pid,DumpVsn) ->
case lookup_index(?proc,Pid) of
[{_,Start}] ->
Fd = open(File),
pos_bof(Fd,Start),
Proc0 =
case DumpVsn of
- "0.0" ->
+ [0,0] ->
%% Old version (translated)
#proc{pid=Pid};
_ ->
@@ -1597,6 +1604,9 @@ get_procinfo(Fd,Fun,Proc) ->
get_procinfo(Fd,Fun,Proc#proc{old_heap_top=val(Fd)});
"Old heap end" ->
get_procinfo(Fd,Fun,Proc#proc{old_heap_end=val(Fd)});
+ "Memory" ->
+ %% stored as integer so we can sort on it
+ get_procinfo(Fd,Fun,Proc#proc{memory=list_to_integer(val(Fd))});
{eof,_} ->
Proc; % truncated file
Other ->
@@ -1865,35 +1875,41 @@ parse(Line0, Dict0) ->
Dict.
-do_sort_procs("state",Procs,"state") ->
+do_sort_procs("state",Procs,#state{sorted="state"}) ->
{lists:reverse(lists:keysort(#proc.state,Procs)),"rstate"};
do_sort_procs("state",Procs,_) ->
{lists:keysort(#proc.state,Procs),"state"};
-do_sort_procs("pid",Procs,"pid") ->
+do_sort_procs("pid",Procs,#state{sorted="pid"}) ->
{lists:reverse(Procs),"rpid"};
do_sort_procs("pid",Procs,_) ->
{Procs,"pid"};
-do_sort_procs("msg_q_len",Procs,"msg_q_len") ->
+do_sort_procs("msg_q_len",Procs,#state{sorted="msg_q_len"}) ->
{lists:keysort(#proc.msg_q_len,Procs),"rmsg_q_len"};
do_sort_procs("msg_q_len",Procs,_) ->
{lists:reverse(lists:keysort(#proc.msg_q_len,Procs)),"msg_q_len"};
-do_sort_procs("reds",Procs,"reds") ->
+do_sort_procs("reds",Procs,#state{sorted="reds"}) ->
{lists:keysort(#proc.reds,Procs),"rreds"};
do_sort_procs("reds",Procs,_) ->
{lists:reverse(lists:keysort(#proc.reds,Procs)),"reds"};
-do_sort_procs("mem",Procs,"mem") ->
- {lists:keysort(#proc.stack_heap,Procs),"rmem"};
-do_sort_procs("mem",Procs,_) ->
- {lists:reverse(lists:keysort(#proc.stack_heap,Procs)),"mem"};
-do_sort_procs("init_func",Procs,"init_func") ->
+do_sort_procs("mem",Procs,#state{sorted="mem",dump_vsn=DumpVsn}) ->
+ KeyPos = if DumpVsn>=?r16b01_dump_vsn -> #proc.memory;
+ true -> #proc.stack_heap
+ end,
+ {lists:keysort(KeyPos,Procs),"rmem"};
+do_sort_procs("mem",Procs,#state{dump_vsn=DumpVsn}) ->
+ KeyPos = if DumpVsn>=?r16b01_dump_vsn -> #proc.memory;
+ true -> #proc.stack_heap
+ end,
+ {lists:reverse(lists:keysort(KeyPos,Procs)),"mem"};
+do_sort_procs("init_func",Procs,#state{sorted="init_func"}) ->
{lists:reverse(lists:keysort(#proc.init_func,Procs)),"rinit_func"};
do_sort_procs("init_func",Procs,_) ->
{lists:keysort(#proc.init_func,Procs),"init_func"};
-do_sort_procs("name_func",Procs,"name_func") ->
+do_sort_procs("name_func",Procs,#state{sorted="name_func"}) ->
{lists:reverse(lists:keysort(#proc.name,Procs)),"rname_func"};
do_sort_procs("name_func",Procs,_) ->
{lists:keysort(#proc.name,Procs),"name_func"};
-do_sort_procs("name",Procs,Sorted) ->
+do_sort_procs("name",Procs,#state{sorted=Sorted}) ->
{No,Yes} =
lists:foldl(fun(P,{N,Y}) ->
case P#proc.name of
@@ -2349,7 +2365,7 @@ allocator_info(File) ->
end,
AllAllocators),
close(Fd),
- R
+ [allocator_summary(R) | R]
end.
get_allocatorinfo(Fd,Start) ->
@@ -2374,6 +2390,213 @@ get_all_vals([],Acc) ->
get_all_vals([Char|Rest],Acc) ->
get_all_vals(Rest,[Char|Acc]).
+%% Calculate allocator summary:
+%%
+%% System totals:
+%% blocks size = sum of mbcs, mbcs_pool and sbcs blocks size over
+%% all allocator instances of all types
+%% carriers size = sum of mbcs, mbcs_pool and sbcs carriers size over
+%% all allocator instances of all types
+%%
+%% I any allocator except sbmbc_alloc has "option e: false" then don't
+%% present system totals.
+%%
+%% For each allocator type:
+%% blocks size = sum of sbmbcs, mbcs, mbcs_pool and sbcs blocks
+%% size over all allocator instances of this type
+%% carriers size = sum of sbmbcs, mbcs, mbcs_pool and sbcs carriers
+%% size over all allocator instances of this type
+%% mseg carriers size = sum of mbcs and sbcs mseg carriers size over all
+%% allocator instances of this type
+%%
+
+-define(sbmbcs_blocks_size,"sbmbcs blocks size").
+-define(mbcs_blocks_size,"mbcs blocks size").
+-define(sbcs_blocks_size,"sbcs blocks size").
+-define(sbmbcs_carriers_size,"sbmbcs carriers size").
+-define(mbcs_carriers_size,"mbcs carriers size").
+-define(sbcs_carriers_size,"sbcs carriers size").
+-define(mbcs_mseg_carriers_size,"mbcs mseg carriers size").
+-define(sbcs_mseg_carriers_size,"sbcs mseg carriers size").
+-define(segments_size,"segments_size").
+-define(mbcs_pool_blocks_size,"mbcs_pool blocks size").
+-define(mbcs_pool_carriers_size,"mbcs_pool carriers size").
+
+-define(type_blocks_size,[?sbmbcs_blocks_size,
+ ?mbcs_blocks_size,
+ ?mbcs_pool_blocks_size,
+ ?sbcs_blocks_size]).
+-define(type_carriers_size,[?sbmbcs_carriers_size,
+ ?mbcs_carriers_size,
+ ?mbcs_pool_carriers_size,
+ ?sbcs_carriers_size]).
+-define(type_mseg_carriers_size,[?mbcs_mseg_carriers_size,
+ ?sbcs_mseg_carriers_size]).
+-define(total_blocks_size,[?mbcs_blocks_size,
+ ?mbcs_pool_blocks_size,
+ ?sbcs_blocks_size]).
+-define(total_carriers_size,[?mbcs_carriers_size,
+ ?mbcs_pool_carriers_size,
+ ?sbcs_carriers_size]).
+-define(total_mseg_carriers_size,[?mbcs_mseg_carriers_size,
+ ?sbcs_mseg_carriers_size]).
+-define(interesting_allocator_info, [?sbmbcs_blocks_size,
+ ?mbcs_blocks_size,
+ ?mbcs_pool_blocks_size,
+ ?sbcs_blocks_size,
+ ?sbmbcs_carriers_size,
+ ?mbcs_carriers_size,
+ ?sbcs_carriers_size,
+ ?mbcs_mseg_carriers_size,
+ ?mbcs_pool_carriers_size,
+ ?sbcs_mseg_carriers_size,
+ ?segments_size]).
+-define(mseg_alloc,"mseg_alloc").
+-define(seg_size,"segments_size").
+-define(sbmbc_alloc,"sbmbc_alloc").
+-define(opt_e_false,{"option e","false"}).
+
+allocator_summary(Allocators) ->
+ {Sorted,DoTotal} = sort_allocator_types(Allocators,[],true),
+ {TypeTotals0,Totals} = sum_allocator_data(Sorted,DoTotal),
+ {TotalMCS,TypeTotals} =
+ case lists:keytake(?mseg_alloc,1,TypeTotals0) of
+ {value,{_,[{?seg_size,SegSize}]},Rest} ->
+ {integer_to_list(SegSize),Rest};
+ false ->
+ {?not_available,TypeTotals0}
+ end,
+ {TotalBS,TotalCS} =
+ case Totals of
+ false ->
+ {?not_available,?not_available};
+ {TBS,TCS} ->
+ {integer_to_list(TBS),integer_to_list(TCS)}
+ end,
+ {{"Summary",["blocks size","carriers size","mseg carriers size"]},
+ [{"total",[TotalBS,TotalCS,TotalMCS]} |
+ format_allocator_summary(lists:reverse(TypeTotals))]}.
+
+format_allocator_summary([{Type,Data}|Rest]) ->
+ [format_allocator_summary(Type,Data) | format_allocator_summary(Rest)];
+format_allocator_summary([]) ->
+ [].
+
+format_allocator_summary(Type,Data) ->
+ BS = get_size_value(blocks_size,Data),
+ CS = get_size_value(carriers_size,Data),
+ MCS = get_size_value(mseg_carriers_size,Data),
+ {Type,[BS,CS,MCS]}.
+
+get_size_value(Key,Data) ->
+ case proplists:get_value(Key,Data) of
+ undefined ->
+ ?not_available;
+ Int ->
+ integer_to_list(Int)
+ end.
+
+%% Sort allocator data per type
+%% Input = [{Instance,[{Key,Data}]}]
+%% Output = [{Type,[{Key,Value}]}]
+%% where Key in Output is one of ?interesting_allocator_info
+%% and Value is the sum over all allocator instances of each type.
+sort_allocator_types([{Name,Data}|Allocators],Acc,DoTotal) ->
+ Type =
+ case string:tokens(Name,"[]") of
+ [T,_Id] -> T;
+ [Name] -> Name
+ end,
+ TypeData = proplists:get_value(Type,Acc,[]),
+ {NewTypeData,NewDoTotal} = sort_type_data(Type,Data,TypeData,DoTotal),
+ NewAcc = lists:keystore(Type,1,Acc,{Type,NewTypeData}),
+ sort_allocator_types(Allocators,NewAcc,NewDoTotal);
+sort_allocator_types([],Acc,DoTotal) ->
+ {Acc,DoTotal}.
+
+sort_type_data(Type,[?opt_e_false|Data],Acc,_) when Type=/=?sbmbc_alloc->
+ sort_type_data(Type,Data,Acc,false);
+sort_type_data(Type,[{Key,Val0}|Data],Acc,DoTotal) ->
+ case lists:member(Key,?interesting_allocator_info) of
+ true ->
+ Val = list_to_integer(hd(Val0)),
+ sort_type_data(Type,Data,update_value(Key,Val,Acc),DoTotal);
+ false ->
+ sort_type_data(Type,Data,Acc,DoTotal)
+ end;
+sort_type_data(_Type,[],Acc,DoTotal) ->
+ {Acc,DoTotal}.
+
+%% Sum up allocator data in total blocks- and carriers size for all
+%% allocators and per type of allocator.
+%% Input = Output from sort_allocator_types/3
+%% Output = {[{"mseg_alloc",[{"segments_size",Value}]},
+%% {Type,[{blocks_size,Value},
+%% {carriers_size,Value},
+%% {mseg_carriers_size,Value}]},
+%% ...],
+%% {TotalBlocksSize,TotalCarriersSize}}
+sum_allocator_data(AllocData,false) ->
+ sum_allocator_data(AllocData,[],false);
+sum_allocator_data(AllocData,true) ->
+ sum_allocator_data(AllocData,[],{0,0}).
+
+sum_allocator_data([{_Type,[]}|AllocData],TypeAcc,Total) ->
+ sum_allocator_data(AllocData,TypeAcc,Total);
+sum_allocator_data([{Type,Data}|AllocData],TypeAcc,Total) ->
+ {TypeSum,NewTotal} = sum_type_data(Data,[],Total),
+ sum_allocator_data(AllocData,[{Type,TypeSum}|TypeAcc],NewTotal);
+sum_allocator_data([],TypeAcc,Total) ->
+ {TypeAcc,Total}.
+
+sum_type_data([{Key,Value}|Data],TypeAcc,Total) ->
+ NewTotal =
+ case Total of
+ false ->
+ false;
+ {TotalBS,TotalCS} ->
+ case lists:member(Key,?total_blocks_size) of
+ true ->
+ {TotalBS+Value,TotalCS};
+ false ->
+ case lists:member(Key,?total_carriers_size) of
+ true ->
+ {TotalBS,TotalCS+Value};
+ false ->
+ {TotalBS,TotalCS}
+ end
+ end
+ end,
+ NewTypeAcc =
+ case lists:member(Key,?type_blocks_size) of
+ true ->
+ update_value(blocks_size,Value,TypeAcc);
+ false ->
+ case lists:member(Key,?type_carriers_size) of
+ true ->
+ update_value(carriers_size,Value,TypeAcc);
+ false ->
+ case lists:member(Key,?type_mseg_carriers_size) of
+ true ->
+ update_value(mseg_carriers_size,Value,TypeAcc);
+ false ->
+ %% "segments_size" for "mseg_alloc"
+ update_value(Key,Value,TypeAcc)
+ end
+ end
+ end,
+ sum_type_data(Data,NewTypeAcc,NewTotal);
+sum_type_data([],TypeAcc,Total) ->
+ {TypeAcc,Total}.
+
+update_value(Key,Value,Acc) ->
+ case lists:keytake(Key,1,Acc) of
+ false ->
+ [{Key,Value}|Acc];
+ {value,{Key,Old},Acc1} ->
+ [{Key,Old+Value}|Acc1]
+ end.
+
%%-----------------------------------------------------------------
%% Page with hash table information
hash_tables(File) ->
diff --git a/lib/observer/src/crashdump_viewer.hrl b/lib/observer/src/crashdump_viewer.hrl
index 466f33b63b..2e0ea5cf96 100644
--- a/lib/observer/src/crashdump_viewer.hrl
+++ b/lib/observer/src/crashdump_viewer.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2013. 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
@@ -18,7 +18,7 @@
%%
-define(space, "&nbsp;").
-define(unknown, "unknown").
-
+-define(r16b01_dump_vsn, [0,2]). % =erl_crash_dump:0.2
-record(menu_item,{index,picture,text,depth,children,state,target}).
@@ -83,6 +83,7 @@
old_heap_start=?space,
old_heap_top=?space,
old_heap_end=?space,
+ memory,
stack_dump=?space}).
-record(port,
diff --git a/lib/observer/src/crashdump_viewer_html.erl b/lib/observer/src/crashdump_viewer_html.erl
index 3151b83bfb..93c1a842b5 100644
--- a/lib/observer/src/crashdump_viewer_html.erl
+++ b/lib/observer/src/crashdump_viewer_html.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2013. 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
@@ -333,7 +333,13 @@ proc_details_body(Heading,Proc,TW,SharedHeap) ->
td("COLSPAN=1",href_proc_port(Proc#proc.parent))]),
tr(
[td("NOWRAP=true",b("Reductions")),
- td("COLSPAN=3",integer_to_list(Proc#proc.reds))]),
+ td("COLSPAN=1",integer_to_list(Proc#proc.reds))] ++
+ case Proc#proc.memory of
+ undefined -> []; % before R16B01
+ Mem ->
+ [td("NOWRAP=true",b("Memory (bytes)")),
+ td("COLSPAN=1",integer_to_list(Mem))]
+ end),
if SharedHeap ->
Stack = case Proc#proc.stack_heap of
-1 -> "unknown";
@@ -815,12 +821,26 @@ allocator_info_body(Heading,Allocators,TW) ->
[heading(Heading,"memory"),
warn(TW),
p(b("Sizes are in bytes")),
- lists:map(fun({SubTitle,Allocator}) ->
+ lists:map(fun({Head,Allocator}) ->
+ TableHead =
+ case Head of
+ {SubTitle,Columns} ->
+ tr("BGCOLOR=\"#8899AA\"",
+ [th("ALIGN=left",
+ font("SIZE=+1",SubTitle)) |
+ lists:map(
+ fun(CH) ->
+ th("ALIGN=right",CH)
+ end,
+ Columns)]);
+ SubTitle ->
+ tr("BGCOLOR=\"#8899AA\"",
+ th("COLSPAN=10 ALIGN=left",
+ font("SIZE=+1",SubTitle)))
+ end,
[table(
"BORDER=4 CELLPADDING=4",
- [tr("BGCOLOR=\"#8899AA\"",
- th("COLSPAN=10 ALIGN=left",
- font("SIZE=+1",SubTitle))) |
+ [TableHead |
lists:map(
fun({Key,Values}) ->
tr([th("ALIGN=left",Key) |
@@ -1243,8 +1263,8 @@ replace_insrt([],[],Acc) ->
%%% Create a page with one table by delivering chunk by chunk to
%%% inets. crashdump_viewer first calls chunk_page/5 once, then
%%% chunk/3 multiple times until all data is delivered.
-chunk_page(processes,SessionId,TW,{Sorted,SharedHeap},FirstChunk) ->
- Columns = procs_summary_table_head(Sorted,SharedHeap),
+chunk_page(processes,SessionId,TW,{Sorted,SharedHeap,DumpVsn},FirstChunk) ->
+ Columns = procs_summary_table_head(Sorted,SharedHeap,DumpVsn),
chunk_page(SessionId, "Process Information", TW, FirstChunk,
"processes", Columns, fun procs_summary_table/1);
chunk_page(ports,SessionId,TW,_,FirstChunk) ->
@@ -1321,35 +1341,45 @@ deliver(SessionId,IoList) ->
%%%-----------------------------------------------------------------
%%% Page specific stuff for chunk pages
-procs_summary_table_head(Sorted,SharedHeap) ->
+procs_summary_table_head(Sorted,SharedHeap,DumpVsn) ->
MemHeading =
- if SharedHeap ->
- "Stack";
+ if DumpVsn>=?r16b01_dump_vsn ->
+ "Memory (bytes)";
true ->
- "Stack+heap"
+ if SharedHeap ->
+ "Stack";
+ true ->
+ "Stack+heap"
+ end
end,
- [procs_summary_table_head("pid","Pid",Sorted),
- procs_summary_table_head("name_func","Name/Spawned as",Sorted),
- procs_summary_table_head("state","State",Sorted),
- procs_summary_table_head("reds","Reductions",Sorted),
- procs_summary_table_head("mem",MemHeading,Sorted),
- procs_summary_table_head("msg_q_len","MsgQ Length",Sorted)].
-
-procs_summary_table_head(_,Text,no_sort) ->
+ [procs_summary_table_head1("pid","Pid",Sorted),
+ procs_summary_table_head1("name_func","Name/Spawned as",Sorted),
+ procs_summary_table_head1("state","State",Sorted),
+ procs_summary_table_head1("reds","Reductions",Sorted),
+ procs_summary_table_head1("mem",MemHeading,Sorted),
+ procs_summary_table_head1("msg_q_len","MsgQ Length",Sorted)].
+
+procs_summary_table_head1(_,Text,no_sort) ->
Text;
-procs_summary_table_head(Sorted,Text,Sorted) ->
+procs_summary_table_head1(Sorted,Text,Sorted) ->
%% Mark the sorted column (bigger and italic)
font("SIZE=\"+1\"",em(href("./sort_procs?sort="++Sorted,Text)));
-procs_summary_table_head(SortOn,Text,_Sorted) ->
+procs_summary_table_head1(SortOn,Text,_Sorted) ->
href("./sort_procs?sort="++SortOn,Text).
procs_summary_table(Proc) ->
#proc{pid=Pid,name=Name,state=State,
- reds=Reds,stack_heap=Mem0,msg_q_len=MsgQLen}=Proc,
- Mem = case Mem0 of
- -1 -> "unknown";
- _ -> integer_to_list(Mem0)
- end,
+ reds=Reds,stack_heap=Stack,memory=Memory,msg_q_len=MsgQLen}=Proc,
+ Mem =
+ case Memory of
+ undefined -> % assuming pre-R16B01
+ case Stack of
+ -1 -> "unknown";
+ _ -> integer_to_list(Stack)
+ end;
+ _ ->
+ integer_to_list(Memory)
+ end,
tr(
[td(href(["./proc_details?pid=",Pid],Pid)),
td(Name),
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 4077f8371a..f7712cf3da 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2013. 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
@@ -122,7 +122,10 @@ fill_info([{Str, Key}|Rest], Data) when is_atom(Key); is_function(Key) ->
[{Str, get_value(Key, Data)} | fill_info(Rest, Data)];
fill_info([{Str, {Format, Key}}|Rest], Data)
when is_atom(Key); is_function(Key), is_atom(Format) ->
- [{Str, {Format, get_value(Key,Data)}} | fill_info(Rest, Data)];
+ case get_value(Key, Data) of
+ undefined -> [{Str, undefined} | fill_info(Rest, Data)];
+ Value -> [{Str, {Format, Value}} | fill_info(Rest, Data)]
+ end;
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) ->
diff --git a/lib/observer/src/observer_sys_wx.erl b/lib/observer/src/observer_sys_wx.erl
index f00a666a35..31800cf12a 100644
--- a/lib/observer/src/observer_sys_wx.erl
+++ b/lib/observer/src/observer_sys_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2013. 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
@@ -37,6 +37,7 @@
parent_notebook,
panel, sizer,
menubar,
+ alloc,
fields,
timer}).
@@ -47,22 +48,28 @@ 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(?wxHORIZONTAL),
+ Sizer = wxBoxSizer:new(?wxVERTICAL),
+ TopSizer = wxBoxSizer:new(?wxHORIZONTAL),
{FPanel0, _FSizer0, Fields0} =
observer_lib:display_info(Panel, observer_lib:fill_info(Info, SysInfo)),
{FPanel1, _FSizer1, Fields1} =
observer_lib:display_info(Panel, observer_lib:fill_info(Stat, SysInfo)),
- wxSizer:add(Sizer, FPanel0, [{flag, ?wxEXPAND bor ?wxTOP bor ?wxBOTTOM bor ?wxLEFT},
- {proportion, 1}, {border, 5}]),
- wxSizer:add(Sizer, FPanel1, [{flag, ?wxEXPAND bor ?wxTOP bor ?wxBOTTOM bor ?wxRIGHT},
- {proportion, 1}, {border, 5}]),
+ wxSizer:add(TopSizer, FPanel0, [{flag, ?wxEXPAND}, {proportion, 1}]),
+ wxSizer:add(TopSizer, FPanel1, [{flag, ?wxEXPAND}, {proportion, 1}]),
+ BorderFlags = ?wxLEFT bor ?wxRIGHT,
+ MemoryInfo = create_mem_info(Panel, AllocInfo),
+ wxSizer:add(Sizer, TopSizer, [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP},
+ {proportion, 0}, {border, 5}]),
+ wxSizer:add(Sizer, MemoryInfo, [{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,
+ panel=Panel, sizer=Sizer, alloc=MemoryInfo,
timer=Timer, fields=Fields0 ++ Fields1}}.
create_sys_menu(Parent) ->
@@ -70,13 +77,84 @@ 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}) ->
+update_syspage(#sys_wx_state{node = Node, fields=Fields, sizer=Sizer, alloc=AllocCtrl}) ->
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(Panel, Fields) ->
+ 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),
+ 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/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl
index d1d291d5cb..670e216d97 100644
--- a/lib/runtime_tools/src/observer_backend.erl
+++ b/lib/runtime_tools/src/observer_backend.erl
@@ -49,6 +49,10 @@ vsn() ->
%% observer backend
%%
sys_info() ->
+ MemInfo = try erlang:memory() of
+ Mem -> Mem
+ catch _:_ -> []
+ end,
{{_,Input},{_,Output}} = erlang:statistics(io),
[{process_count, erlang:system_info(process_count)},
{process_limit, erlang:system_info(process_limit)},
@@ -68,9 +72,16 @@ sys_info() ->
{threads, erlang:system_info(threads)},
{thread_pool_size, erlang:system_info(thread_pool_size)},
{wordsize_internal, erlang:system_info({wordsize, internal})},
- {wordsize_external, erlang:system_info({wordsize, external})} |
- erlang:memory()
- ].
+ {wordsize_external, erlang:system_info({wordsize, external})},
+ {alloc_info, alloc_info()}
+ | MemInfo].
+
+alloc_info() ->
+ {_,_,AllocTypes,_} = erlang:system_info(allocator),
+ try erlang:system_info({allocator_sizes,AllocTypes}) of
+ Allocators -> Allocators
+ catch _:_ -> []
+ end.
get_table(Parent, Table, Module) ->
spawn(fun() ->
@@ -274,7 +285,7 @@ etop_collect([P|Ps], Acc) ->
[{registered_name,Reg},{initial_call,Initial},{memory,Mem},
{reductions,Reds},{current_function,Current},{message_queue_len,Qlen}] ->
Name = case Reg of
- [] -> Initial;
+ [] -> initial_call(Initial, P);
_ -> Reg
end,
Info = #etop_proc_info{pid=P,mem=Mem,reds=Reds,name=Name,
@@ -283,6 +294,11 @@ etop_collect([P|Ps], Acc) ->
end;
etop_collect([], Acc) -> Acc.
+initial_call({proc_lib, init_p, _}, Pid) ->
+ proc_lib:translate_initial_call(Pid);
+initial_call(Initial, _Pid) ->
+ Initial.
+
%%
%% ttb backend
%%