From 9da4c7b4e41c325569d2576fa4c55568a1f1cfa2 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 17 Dec 2013 11:11:17 +0100 Subject: observer: renamed crashdump_viewer files and fixed makefiles --- lib/observer/src/Makefile | 46 ++- lib/observer/src/cdv_atom_cb.erl | 48 +++ lib/observer/src/cdv_atom_wx.erl | 48 --- lib/observer/src/cdv_bin_cb.erl | 82 +++++ lib/observer/src/cdv_bin_wx.erl | 82 ----- lib/observer/src/cdv_detail_win.erl | 158 ---------- lib/observer/src/cdv_detail_wx.erl | 158 ++++++++++ lib/observer/src/cdv_dist_cb.erl | 91 ++++++ lib/observer/src/cdv_dist_wx.erl | 91 ------ lib/observer/src/cdv_ets_cb.erl | 67 +++++ lib/observer/src/cdv_ets_wx.erl | 67 ----- lib/observer/src/cdv_fun_cb.erl | 58 ++++ lib/observer/src/cdv_fun_wx.erl | 58 ---- lib/observer/src/cdv_gen_cb.erl | 45 +++ lib/observer/src/cdv_gen_wx.erl | 45 --- lib/observer/src/cdv_html_page.erl | 128 -------- lib/observer/src/cdv_html_wx.erl | 128 ++++++++ lib/observer/src/cdv_info_page.erl | 128 -------- lib/observer/src/cdv_info_wx.erl | 128 ++++++++ lib/observer/src/cdv_int_tab_cb.erl | 86 ++++++ lib/observer/src/cdv_int_tab_wx.erl | 86 ------ lib/observer/src/cdv_mem_cb.erl | 84 ++++++ lib/observer/src/cdv_mem_wx.erl | 84 ------ lib/observer/src/cdv_mod_cb.erl | 102 +++++++ lib/observer/src/cdv_mod_wx.erl | 102 ------- lib/observer/src/cdv_multi_panel.erl | 188 ------------ lib/observer/src/cdv_multi_wx.erl | 188 ++++++++++++ lib/observer/src/cdv_port_cb.erl | 103 +++++++ lib/observer/src/cdv_port_wx.erl | 102 ------- lib/observer/src/cdv_proc_cb.erl | 156 ++++++++++ lib/observer/src/cdv_proc_wx.erl | 155 ---------- lib/observer/src/cdv_table_page.erl | 106 ------- lib/observer/src/cdv_table_wx.erl | 106 +++++++ lib/observer/src/cdv_term_cb.erl | 75 +++++ lib/observer/src/cdv_term_wx.erl | 75 ----- lib/observer/src/cdv_timer_cb.erl | 51 ++++ lib/observer/src/cdv_timer_wx.erl | 51 ---- lib/observer/src/cdv_virtual_list.erl | 414 -------------------------- lib/observer/src/cdv_virtual_list_wx.erl | 414 ++++++++++++++++++++++++++ lib/observer/src/cdv_wx.erl | 462 +++++++++++++++++++++++++++++ lib/observer/src/crashdump_viewer.erl | 10 +- lib/observer/src/crashdump_viewer_html.erl | 388 ------------------------ lib/observer/src/crashdump_viewer_wx.erl | 462 ----------------------------- lib/observer/src/observer.app.src | 24 +- lib/observer/src/observer_html_lib.erl | 388 ++++++++++++++++++++++++ lib/observer/src/observer_procinfo.erl | 12 +- 46 files changed, 3075 insertions(+), 3055 deletions(-) create mode 100644 lib/observer/src/cdv_atom_cb.erl delete mode 100644 lib/observer/src/cdv_atom_wx.erl create mode 100644 lib/observer/src/cdv_bin_cb.erl delete mode 100644 lib/observer/src/cdv_bin_wx.erl delete mode 100644 lib/observer/src/cdv_detail_win.erl create mode 100644 lib/observer/src/cdv_detail_wx.erl create mode 100644 lib/observer/src/cdv_dist_cb.erl delete mode 100644 lib/observer/src/cdv_dist_wx.erl create mode 100644 lib/observer/src/cdv_ets_cb.erl delete mode 100644 lib/observer/src/cdv_ets_wx.erl create mode 100644 lib/observer/src/cdv_fun_cb.erl delete mode 100644 lib/observer/src/cdv_fun_wx.erl create mode 100644 lib/observer/src/cdv_gen_cb.erl delete mode 100644 lib/observer/src/cdv_gen_wx.erl delete mode 100644 lib/observer/src/cdv_html_page.erl create mode 100644 lib/observer/src/cdv_html_wx.erl delete mode 100644 lib/observer/src/cdv_info_page.erl create mode 100644 lib/observer/src/cdv_info_wx.erl create mode 100644 lib/observer/src/cdv_int_tab_cb.erl delete mode 100644 lib/observer/src/cdv_int_tab_wx.erl create mode 100644 lib/observer/src/cdv_mem_cb.erl delete mode 100644 lib/observer/src/cdv_mem_wx.erl create mode 100644 lib/observer/src/cdv_mod_cb.erl delete mode 100644 lib/observer/src/cdv_mod_wx.erl delete mode 100644 lib/observer/src/cdv_multi_panel.erl create mode 100644 lib/observer/src/cdv_multi_wx.erl create mode 100644 lib/observer/src/cdv_port_cb.erl delete mode 100644 lib/observer/src/cdv_port_wx.erl create mode 100644 lib/observer/src/cdv_proc_cb.erl delete mode 100644 lib/observer/src/cdv_proc_wx.erl delete mode 100644 lib/observer/src/cdv_table_page.erl create mode 100644 lib/observer/src/cdv_table_wx.erl create mode 100644 lib/observer/src/cdv_term_cb.erl delete mode 100644 lib/observer/src/cdv_term_wx.erl create mode 100644 lib/observer/src/cdv_timer_cb.erl delete mode 100644 lib/observer/src/cdv_timer_wx.erl delete mode 100644 lib/observer/src/cdv_virtual_list.erl create mode 100644 lib/observer/src/cdv_virtual_list_wx.erl create mode 100644 lib/observer/src/cdv_wx.erl delete mode 100644 lib/observer/src/crashdump_viewer_html.erl delete mode 100644 lib/observer/src/crashdump_viewer_wx.erl create mode 100644 lib/observer/src/observer_html_lib.erl (limited to 'lib/observer/src') diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile index 0b1813db0a..fc6f51c617 100644 --- a/lib/observer/src/Makefile +++ b/lib/observer/src/Makefile @@ -36,36 +36,34 @@ RELSYSDIR = $(RELEASE_PATH)/lib/observer-$(VSN) MODULES= \ crashdump_viewer \ - crashdump_viewer_html \ - crashdump_viewer_wx \ - cdv_info_page \ - cdv_virtual_list \ - cdv_detail_win \ - cdv_table_page \ - cdv_multi_panel \ - cdv_html_page \ - cdv_gen_wx \ - cdv_proc_wx \ - cdv_port_wx \ - cdv_ets_multi_wx \ - cdv_ets_wx \ - cdv_timer_wx \ - cdv_fun_wx \ - cdv_atom_wx \ - cdv_dist_wx \ - cdv_mod_wx \ - cdv_mem_wx \ - cdv_int_tab_wx \ - cdv_bin_wx \ - cdv_term_wx \ + cdv_atom_cb \ + cdv_bin_cb \ + cdv_detail_wx \ + cdv_dist_cb \ + cdv_ets_cb \ + cdv_fun_cb \ + cdv_gen_cb \ + cdv_html_wx \ + cdv_info_wx \ + cdv_int_tab_cb \ + cdv_mem_cb \ + cdv_mod_cb \ + cdv_multi_wx \ + cdv_port_cb \ + cdv_proc_cb \ + cdv_table_wx \ + cdv_term_cb \ + cdv_timer_cb \ + cdv_virtual_list_wx \ + cdv_wx \ etop \ etop_gui \ etop_tr \ etop_txt \ observer \ observer_app_wx \ + observer_html_lib \ observer_lib \ - observer_wx \ observer_perf_wx \ observer_pro_wx \ observer_procinfo \ @@ -74,7 +72,7 @@ MODULES= \ observer_traceoptions_wx \ observer_tv_table \ observer_tv_wx \ - observer_term_wx \ + observer_wx \ ttb \ ttb_et diff --git a/lib/observer/src/cdv_atom_cb.erl b/lib/observer/src/cdv_atom_cb.erl new file mode 100644 index 0000000000..46fce81b52 --- /dev/null +++ b/lib/observer/src/cdv_atom_cb.erl @@ -0,0 +1,48 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_atom_cb). + +-export([col_to_elem/1, + col_spec/0, + get_info/1, + format/1]). + +-include_lib("wx/include/wx.hrl"). + +%% Defines +-define(COL_ID, 0). +-define(COL_ATOM, ?COL_ID+1). + +%% Callbacks for cdv_virtual_list_wx +col_to_elem(id) -> col_to_elem(?COL_ID); +col_to_elem(Id) -> Id+1. + +col_spec() -> + [{"Creation order", ?wxLIST_FORMAT_CENTER, 100}, + {"Atom", ?wxLIST_FORMAT_LEFT, 100}]. + +get_info(_) -> + {ok,Info,TW} = crashdump_viewer:atoms(), + {Info,TW}. + +format({Bin,q}) when is_binary(Bin) -> + [$'|binary_to_list(Bin)]; +format({Bin,nq}) when is_binary(Bin) -> + lists:flatten(io_lib:format("~ts",[Bin])); +format(D) -> + D. diff --git a/lib/observer/src/cdv_atom_wx.erl b/lib/observer/src/cdv_atom_wx.erl deleted file mode 100644 index 372badc944..0000000000 --- a/lib/observer/src/cdv_atom_wx.erl +++ /dev/null @@ -1,48 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_atom_wx). - --export([col_to_elem/1, - col_spec/0, - get_info/1, - format/1]). - --include_lib("wx/include/wx.hrl"). - -%% Defines --define(COL_ID, 0). --define(COL_ATOM, ?COL_ID+1). - -%% Callbacks for cdv_virtual_list -col_to_elem(id) -> col_to_elem(?COL_ID); -col_to_elem(Id) -> Id+1. - -col_spec() -> - [{"Creation order", ?wxLIST_FORMAT_CENTER, 100}, - {"Atom", ?wxLIST_FORMAT_LEFT, 100}]. - -get_info(_) -> - {ok,Info,TW} = crashdump_viewer:atoms(), - {Info,TW}. - -format({Bin,q}) when is_binary(Bin) -> - [$'|binary_to_list(Bin)]; -format({Bin,nq}) when is_binary(Bin) -> - lists:flatten(io_lib:format("~ts",[Bin])); -format(D) -> - D. diff --git a/lib/observer/src/cdv_bin_cb.erl b/lib/observer/src/cdv_bin_cb.erl new file mode 100644 index 0000000000..0266047ee3 --- /dev/null +++ b/lib/observer/src/cdv_bin_cb.erl @@ -0,0 +1,82 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_bin_cb). + +-export([get_details/1, + detail_pages/0]). + +%% Callbacks for cdv_detail_wx +get_details({_, {T,Key}}) -> + [{Key,Term}] = ets:lookup(T,Key), + {ok,{"Expanded Binary", Term, []}}; +get_details({cdv, Id}) -> + {ok,Bin} = crashdump_viewer:expand_binary(Id), + {ok,{"Expanded Binary", Bin, []}}. + +detail_pages() -> + [{"Binary", fun init_bin_page/2}]. + +init_bin_page(Parent,Bin) -> + cdv_multi_wx:start_link( + Parent, + [{"Format \~p",cdv_html_wx,format_bin_fun("~p",Bin)}, + {"Format \~tp",cdv_html_wx,format_bin_fun("~tp",Bin)}, + {"Format \~w",cdv_html_wx,format_bin_fun("~w",Bin)}, + {"Format \~s",cdv_html_wx,format_bin_fun("~s",Bin)}, + {"Format \~ts",cdv_html_wx,format_bin_fun("~ts",Bin)}, + {"Hex",cdv_html_wx,hex_binary_fun(Bin)}, + {"Term",cdv_html_wx,binary_to_term_fun(Bin)}]). + +format_bin_fun(Format,Bin) -> + fun() -> + try io_lib:format(Format,[Bin]) of + Str -> plain_html(lists:flatten(Str)) + catch error:badarg -> + Warning = "This binary can not be formatted with " ++ Format, + observer_html_lib:warning(Warning) + end + end. + +binary_to_term_fun(Bin) -> + fun() -> + try binary_to_term(Bin) of + Term -> plain_html(io_lib:format("~p",[Term])) + catch error:badarg -> + Warning = "This binary can not be coverted to an Erlang term", + observer_html_lib:warning(Warning) + end + end. + +-define(line_break,25). +hex_binary_fun(Bin) -> + fun() -> + S = "<<" ++ format_hex(Bin,?line_break) ++ ">>", + plain_html(io_lib:format("~s",[S])) + end. + +format_hex(<>,_) -> + [integer_to_list(B1,16),integer_to_list(B2,16)]; +format_hex(<>,0) -> + [integer_to_list(B1,16),integer_to_list(B2,16),$,,$\n,$\s,$\s + | format_hex(Bin,?line_break)]; +format_hex(<>,N) -> + [integer_to_list(B1,16),integer_to_list(B2,16),$, + | format_hex(Bin,N-1)]. + +plain_html(Text) -> + observer_html_lib:plain_page(Text). diff --git a/lib/observer/src/cdv_bin_wx.erl b/lib/observer/src/cdv_bin_wx.erl deleted file mode 100644 index 6cf344f945..0000000000 --- a/lib/observer/src/cdv_bin_wx.erl +++ /dev/null @@ -1,82 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_bin_wx). - --export([get_details/1, - detail_pages/0]). - -%% Callbacks for cdv_detail_win -get_details({_, {T,Key}}) -> - [{Key,Term}] = ets:lookup(T,Key), - {ok,{"Expanded Binary", Term, []}}; -get_details({cdv, Id}) -> - {ok,Bin} = crashdump_viewer:expand_binary(Id), - {ok,{"Expanded Binary", Bin, []}}. - -detail_pages() -> - [{"Binary", fun init_bin_page/2}]. - -init_bin_page(Parent,Bin) -> - cdv_multi_panel:start_link( - Parent, - [{"Format \~p",cdv_html_page,format_bin_fun("~p",Bin)}, - {"Format \~tp",cdv_html_page,format_bin_fun("~tp",Bin)}, - {"Format \~w",cdv_html_page,format_bin_fun("~w",Bin)}, - {"Format \~s",cdv_html_page,format_bin_fun("~s",Bin)}, - {"Format \~ts",cdv_html_page,format_bin_fun("~ts",Bin)}, - {"Hex",cdv_html_page,hex_binary_fun(Bin)}, - {"Term",cdv_html_page, binary_to_term_fun(Bin)}]). - -format_bin_fun(Format,Bin) -> - fun() -> - try io_lib:format(Format,[Bin]) of - Str -> plain_html(lists:flatten(Str)) - catch error:badarg -> - Warning = "This binary can not be formatted with " ++ Format, - crashdump_viewer_html:warning(Warning) - end - end. - -binary_to_term_fun(Bin) -> - fun() -> - try binary_to_term(Bin) of - Term -> plain_html(io_lib:format("~p",[Term])) - catch error:badarg -> - Warning = "This binary can not be coverted to an Erlang term", - crashdump_viewer_html:warning(Warning) - end - end. - --define(line_break,25). -hex_binary_fun(Bin) -> - fun() -> - S = "<<" ++ format_hex(Bin,?line_break) ++ ">>", - plain_html(io_lib:format("~s",[S])) - end. - -format_hex(<>,_) -> - [integer_to_list(B1,16),integer_to_list(B2,16)]; -format_hex(<>,0) -> - [integer_to_list(B1,16),integer_to_list(B2,16),$,,$\n,$\s,$\s - | format_hex(Bin,?line_break)]; -format_hex(<>,N) -> - [integer_to_list(B1,16),integer_to_list(B2,16),$, - | format_hex(Bin,N-1)]. - -plain_html(Text) -> - crashdump_viewer_html:plain_page(Text). diff --git a/lib/observer/src/cdv_detail_win.erl b/lib/observer/src/cdv_detail_win.erl deleted file mode 100644 index 4445169379..0000000000 --- a/lib/observer/src/cdv_detail_win.erl +++ /dev/null @@ -1,158 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_detail_win). - --behaviour(wx_object). - --export([start_link/3]). - --export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, - handle_call/3, handle_info/2]). - --include_lib("wx/include/wx.hrl"). --include("crashdump_viewer.hrl"). --include("observer_defs.hrl"). - --record(state, {parent, - frame, - id, - pages=[] - }). - -%% Defines --define(ID_NOTEBOOK, 604). - -%% Detail view -start_link(Id, ParentFrame, Callback) -> - wx_object:start_link(?MODULE, [Id, ParentFrame, Callback, self()], []). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -init([Id, ParentFrame, Callback, Parent]) -> - case Callback:get_details(Id) of - {ok,Details} -> - init(Id,ParentFrame,Callback,Parent,Details); - {yes_no, Info, Fun} -> - case observer_lib:display_yes_no_dialog(Info) of - ?wxID_YES -> Fun(); - ?wxID_NO -> ok - end, - {stop,normal}; - {info,Info} -> - observer_lib:display_info_dialog(Info), - {stop,normal} - end. - -init(Id,ParentFrame,Callback,Parent,{Title,Info,TW}) -> - Frame=wxFrame:new(ParentFrame, ?wxID_ANY, [Title], - [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {850,600}}]), - MenuBar = wxMenuBar:new(), - create_menus(MenuBar), - wxFrame:setMenuBar(Frame, MenuBar), - - Panel = wxPanel:new(Frame, []), - Sizer = wxBoxSizer:new(?wxVERTICAL), - {InfoPanel,Pages} = create_pages(Panel,Callback:detail_pages(),[Info]), - wxSizer:add(Sizer, InfoPanel, [{proportion, 1}, {flag, ?wxEXPAND}]), - - case TW of - [] -> - undefined; - _ -> - StatusBar = observer_lib:create_status_bar(Panel), - wxSizer:add(Sizer, StatusBar, [{flag, ?wxEXPAND bor ?wxALL}, - {proportion, 0}, - {border,4}]), - wxTextCtrl:writeText(StatusBar, TW), - StatusBar - end, - - wxPanel:setSizer(Panel, Sizer), - - wxFrame:connect(Frame, close_window), - wxMenu:connect(Frame, command_menu_selected), - wxFrame:show(Frame), - {Frame, #state{parent=Parent, - id=Id, - frame=Frame, - pages=Pages - }}. - -create_pages(Panel,[{_PageTitle,Fun}],FunArgs) -> - %% Only one page - don't create notebook - Page = init_panel(Panel, Fun, FunArgs), - {Page,[Page]}; -create_pages(Panel,PageSpecs,FunArgs) -> - Notebook = wxNotebook:new(Panel, ?ID_NOTEBOOK, [{style, ?wxBK_DEFAULT}]), - Pages = [init_tab(Notebook, PageTitle, Fun, FunArgs) - || {PageTitle,Fun} <- PageSpecs], - {Notebook, Pages}. - -init_tab(Notebook,Title,Fun,FunArgs) -> - Panel = init_panel(Notebook,Fun,FunArgs), - true = wxNotebook:addPage(Notebook, Panel, Title), - Panel. - -init_panel(ParentWin, Fun, FunArgs) -> - Panel = wxScrolledWindow:new(ParentWin), - wxScrolledWindow:enableScrolling(Panel,true,true), - wxScrolledWindow:setScrollbars(Panel,1,1,0,0), - Sizer = wxBoxSizer:new(?wxHORIZONTAL), - Window = apply(Fun, [Panel | FunArgs]), - wxSizer:add(Sizer, Window, [{flag, ?wxEXPAND bor ?wxALL}, - {proportion, 1}, - {border, 5}]), - wxPanel:setSizer(Panel, Sizer), - Panel. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Callbacks%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -handle_event(#wx{event=#wxClose{type=close_window}}, State) -> - {stop, normal, State}; - -handle_event(#wx{id=?wxID_CLOSE, event=#wxCommand{type=command_menu_selected}}, - State) -> - {stop, normal, State}; - -handle_event(Event, _State) -> - error({unhandled_event, Event}). - -handle_info(_Info, State) -> - %% io:format("~p: ~p, Handle info: ~p~n", [?MODULE, ?LINE, _Info]), - {noreply, State}. - -handle_call(Call, From, _State) -> - error({unhandled_call, Call, From}). - -handle_cast(Cast, _State) -> - error({unhandled_cast, Cast}). - -terminate(_Reason, #state{parent=Parent,id=Id,frame=Frame}) -> - wx_object:cast(Parent,{detail_win_closed, Id}), - case Frame of - undefined -> ok; - _ -> wxFrame:destroy(Frame) - end, - ok. - -code_change(_, _, State) -> - {ok, State}. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -create_menus(MenuBar) -> - Menus = [{"File", [#create_menu{id=?wxID_CLOSE, text="Close"}]}], - observer_lib:create_menus(Menus, MenuBar, new_window). diff --git a/lib/observer/src/cdv_detail_wx.erl b/lib/observer/src/cdv_detail_wx.erl new file mode 100644 index 0000000000..dc93507a36 --- /dev/null +++ b/lib/observer/src/cdv_detail_wx.erl @@ -0,0 +1,158 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_detail_wx). + +-behaviour(wx_object). + +-export([start_link/3]). + +-export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, + handle_call/3, handle_info/2]). + +-include_lib("wx/include/wx.hrl"). +-include("crashdump_viewer.hrl"). +-include("observer_defs.hrl"). + +-record(state, {parent, + frame, + id, + pages=[] + }). + +%% Defines +-define(ID_NOTEBOOK, 604). + +%% Detail view +start_link(Id, ParentFrame, Callback) -> + wx_object:start_link(?MODULE, [Id, ParentFrame, Callback, self()], []). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([Id, ParentFrame, Callback, Parent]) -> + case Callback:get_details(Id) of + {ok,Details} -> + init(Id,ParentFrame,Callback,Parent,Details); + {yes_no, Info, Fun} -> + case observer_lib:display_yes_no_dialog(Info) of + ?wxID_YES -> Fun(); + ?wxID_NO -> ok + end, + {stop,normal}; + {info,Info} -> + observer_lib:display_info_dialog(Info), + {stop,normal} + end. + +init(Id,ParentFrame,Callback,Parent,{Title,Info,TW}) -> + Frame=wxFrame:new(ParentFrame, ?wxID_ANY, [Title], + [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {850,600}}]), + MenuBar = wxMenuBar:new(), + create_menus(MenuBar), + wxFrame:setMenuBar(Frame, MenuBar), + + Panel = wxPanel:new(Frame, []), + Sizer = wxBoxSizer:new(?wxVERTICAL), + {InfoPanel,Pages} = create_pages(Panel,Callback:detail_pages(),[Info]), + wxSizer:add(Sizer, InfoPanel, [{proportion, 1}, {flag, ?wxEXPAND}]), + + case TW of + [] -> + undefined; + _ -> + StatusBar = observer_lib:create_status_bar(Panel), + wxSizer:add(Sizer, StatusBar, [{flag, ?wxEXPAND bor ?wxALL}, + {proportion, 0}, + {border,4}]), + wxTextCtrl:writeText(StatusBar, TW), + StatusBar + end, + + wxPanel:setSizer(Panel, Sizer), + + wxFrame:connect(Frame, close_window), + wxMenu:connect(Frame, command_menu_selected), + wxFrame:show(Frame), + {Frame, #state{parent=Parent, + id=Id, + frame=Frame, + pages=Pages + }}. + +create_pages(Panel,[{_PageTitle,Fun}],FunArgs) -> + %% Only one page - don't create notebook + Page = init_panel(Panel, Fun, FunArgs), + {Page,[Page]}; +create_pages(Panel,PageSpecs,FunArgs) -> + Notebook = wxNotebook:new(Panel, ?ID_NOTEBOOK, [{style, ?wxBK_DEFAULT}]), + Pages = [init_tab(Notebook, PageTitle, Fun, FunArgs) + || {PageTitle,Fun} <- PageSpecs], + {Notebook, Pages}. + +init_tab(Notebook,Title,Fun,FunArgs) -> + Panel = init_panel(Notebook,Fun,FunArgs), + true = wxNotebook:addPage(Notebook, Panel, Title), + Panel. + +init_panel(ParentWin, Fun, FunArgs) -> + Panel = wxScrolledWindow:new(ParentWin), + wxScrolledWindow:enableScrolling(Panel,true,true), + wxScrolledWindow:setScrollbars(Panel,1,1,0,0), + Sizer = wxBoxSizer:new(?wxHORIZONTAL), + Window = apply(Fun, [Panel | FunArgs]), + wxSizer:add(Sizer, Window, [{flag, ?wxEXPAND bor ?wxALL}, + {proportion, 1}, + {border, 5}]), + wxPanel:setSizer(Panel, Sizer), + Panel. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Callbacks%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +handle_event(#wx{event=#wxClose{type=close_window}}, State) -> + {stop, normal, State}; + +handle_event(#wx{id=?wxID_CLOSE, event=#wxCommand{type=command_menu_selected}}, + State) -> + {stop, normal, State}; + +handle_event(Event, _State) -> + error({unhandled_event, Event}). + +handle_info(_Info, State) -> + %% io:format("~p: ~p, Handle info: ~p~n", [?MODULE, ?LINE, _Info]), + {noreply, State}. + +handle_call(Call, From, _State) -> + error({unhandled_call, Call, From}). + +handle_cast(Cast, _State) -> + error({unhandled_cast, Cast}). + +terminate(_Reason, #state{parent=Parent,id=Id,frame=Frame}) -> + wx_object:cast(Parent,{detail_win_closed, Id}), + case Frame of + undefined -> ok; + _ -> wxFrame:destroy(Frame) + end, + ok. + +code_change(_, _, State) -> + {ok, State}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +create_menus(MenuBar) -> + Menus = [{"File", [#create_menu{id=?wxID_CLOSE, text="Close"}]}], + observer_lib:create_menus(Menus, MenuBar, new_window). diff --git a/lib/observer/src/cdv_dist_cb.erl b/lib/observer/src/cdv_dist_cb.erl new file mode 100644 index 0000000000..3860324d6f --- /dev/null +++ b/lib/observer/src/cdv_dist_cb.erl @@ -0,0 +1,91 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_dist_cb). + +-export([col_to_elem/1, + col_spec/0, + get_info/1, + get_detail_cols/1, + get_details/1, + detail_pages/0]). + +-include_lib("wx/include/wx.hrl"). +-include("crashdump_viewer.hrl"). + +%% Columns +-define(COL_NAME, 0). +-define(COL_TYPE, ?COL_NAME+1). +-define(COL_CTRL, ?COL_TYPE+1). +-define(COL_CH, ?COL_CTRL+1). +-define(COL_CRE, ?COL_CH+1). + +%% Callbacks for cdv_virtual_list_wx +col_to_elem(id) -> col_to_elem(?COL_CH); +col_to_elem(?COL_NAME) -> #nod.name; +col_to_elem(?COL_CH) -> #nod.channel; +col_to_elem(?COL_CTRL) -> #nod.controller; +col_to_elem(?COL_CRE) -> #nod.creation; +col_to_elem(?COL_TYPE) -> #nod.conn_type. + +col_spec() -> + [{"Name", ?wxLIST_FORMAT_LEFT, 300}, + {"Connection type", ?wxLIST_FORMAT_LEFT, 130}, + {"Controller", ?wxLIST_FORMAT_LEFT, 130}, + {"Channel", ?wxLIST_FORMAT_RIGHT, 80}, + {"Creation", ?wxLIST_FORMAT_RIGHT, 80}]. + +get_info(_) -> + {ok,Info,TW} = crashdump_viewer:dist_info(), + {Info,TW}. + +get_detail_cols(_) -> + {[?COL_CH,?COL_CTRL],true}. + +%% Callbacks for cdv_detail_wx +get_details(Id) -> + case crashdump_viewer:node_info(Id) of + {ok,Info,TW} -> + Proplist = crashdump_viewer:to_proplist(record_info(fields,nod),Info), + Title = io_lib:format("~s (~s)",[Info#nod.name,Id]), + {ok,{Title,Proplist,TW}}; + {error,not_found} -> + Info = "The node you are searching for could not be found.", + {info,Info} + end. + +detail_pages() -> + [{"General Information", fun init_gen_page/2}]. + +init_gen_page(Parent, Info) -> + Fields = info_fields(), + cdv_info_wx:start_link(Parent,{Fields,Info,[]}). + +%%%----------------------------------------------------------------- +%%% Internal +info_fields() -> + [{"Overview", + [{"Name", name}, + {"Type", conn_type}, + {"Channel", channel}, + {"Controller", {click,controller}}, + {"Creation", creation}, + {"Extra Info", error}]}, + {scroll_boxes, + [{"Remote Links",1,{click,remote_links}}, + {"Remote Monitors",1,{click,remote_mon}}, + {"Remote Monitored By",1,{click,remote_mon_by}}]}]. diff --git a/lib/observer/src/cdv_dist_wx.erl b/lib/observer/src/cdv_dist_wx.erl deleted file mode 100644 index 3910f4fac5..0000000000 --- a/lib/observer/src/cdv_dist_wx.erl +++ /dev/null @@ -1,91 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_dist_wx). - --export([col_to_elem/1, - col_spec/0, - get_info/1, - get_detail_cols/1, - get_details/1, - detail_pages/0]). - --include_lib("wx/include/wx.hrl"). --include("crashdump_viewer.hrl"). - -%% Columns --define(COL_NAME, 0). --define(COL_TYPE, ?COL_NAME+1). --define(COL_CTRL, ?COL_TYPE+1). --define(COL_CH, ?COL_CTRL+1). --define(COL_CRE, ?COL_CH+1). - -%% Callbacks for cdv_virtual_list -col_to_elem(id) -> col_to_elem(?COL_CH); -col_to_elem(?COL_NAME) -> #nod.name; -col_to_elem(?COL_CH) -> #nod.channel; -col_to_elem(?COL_CTRL) -> #nod.controller; -col_to_elem(?COL_CRE) -> #nod.creation; -col_to_elem(?COL_TYPE) -> #nod.conn_type. - -col_spec() -> - [{"Name", ?wxLIST_FORMAT_LEFT, 300}, - {"Connection type", ?wxLIST_FORMAT_LEFT, 130}, - {"Controller", ?wxLIST_FORMAT_LEFT, 130}, - {"Channel", ?wxLIST_FORMAT_RIGHT, 80}, - {"Creation", ?wxLIST_FORMAT_RIGHT, 80}]. - -get_info(_) -> - {ok,Info,TW} = crashdump_viewer:dist_info(), - {Info,TW}. - -get_detail_cols(_) -> - {[?COL_CH,?COL_CTRL],true}. - -%% Callbacks for cdv_detail_win -get_details(Id) -> - case crashdump_viewer:node_info(Id) of - {ok,Info,TW} -> - Proplist = crashdump_viewer:to_proplist(record_info(fields,nod),Info), - Title = io_lib:format("~s (~s)",[Info#nod.name,Id]), - {ok,{Title,Proplist,TW}}; - {error,not_found} -> - Info = "The node you are searching for could not be found.", - {info,Info} - end. - -detail_pages() -> - [{"General Information", fun init_gen_page/2}]. - -init_gen_page(Parent, Info) -> - Fields = info_fields(), - cdv_info_page:start_link(Parent,{Fields,Info,[]}). - -%%%----------------------------------------------------------------- -%%% Internal -info_fields() -> - [{"Overview", - [{"Name", name}, - {"Type", conn_type}, - {"Channel", channel}, - {"Controller", {click,controller}}, - {"Creation", creation}, - {"Extra Info", error}]}, - {scroll_boxes, - [{"Remote Links",1,{click,remote_links}}, - {"Remote Monitors",1,{click,remote_mon}}, - {"Remote Monitored By",1,{click,remote_mon_by}}]}]. diff --git a/lib/observer/src/cdv_ets_cb.erl b/lib/observer/src/cdv_ets_cb.erl new file mode 100644 index 0000000000..2a5c170e58 --- /dev/null +++ b/lib/observer/src/cdv_ets_cb.erl @@ -0,0 +1,67 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_ets_cb). + +-export([col_to_elem/1, + col_spec/0, + get_info/1, + get_detail_cols/1]). + +-include_lib("wx/include/wx.hrl"). +-include("crashdump_viewer.hrl"). + +%% Defines +-define(COL_ID, 0). +-define(COL_NAME, ?COL_ID+1). +-define(COL_SLOT, ?COL_NAME+1). +-define(COL_OWNER, ?COL_SLOT+1). +-define(COL_BUCK, ?COL_OWNER+1). +-define(COL_OBJ, ?COL_BUCK+1). +-define(COL_MEM, ?COL_OBJ+1). +-define(COL_TYPE, ?COL_MEM+1). + +%% Callbacks for cdv_virtual_list_wx +col_to_elem(id) -> col_to_elem(?COL_ID); +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_BUCK) -> #ets_table.buckets; +col_to_elem(?COL_OBJ) -> #ets_table.size; +col_to_elem(?COL_MEM) -> #ets_table.memory. + +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} + ]. + +get_info(Owner) -> + {ok,Info,TW} = crashdump_viewer:ets_tables(Owner), + {Info,TW}. + +get_detail_cols(all) -> + {[?COL_OWNER],false}; +get_detail_cols(_) -> + {[],false}. diff --git a/lib/observer/src/cdv_ets_wx.erl b/lib/observer/src/cdv_ets_wx.erl deleted file mode 100644 index ac45aa297e..0000000000 --- a/lib/observer/src/cdv_ets_wx.erl +++ /dev/null @@ -1,67 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_ets_wx). - --export([col_to_elem/1, - col_spec/0, - get_info/1, - get_detail_cols/1]). - --include_lib("wx/include/wx.hrl"). --include("crashdump_viewer.hrl"). - -%% Defines --define(COL_ID, 0). --define(COL_NAME, ?COL_ID+1). --define(COL_SLOT, ?COL_NAME+1). --define(COL_OWNER, ?COL_SLOT+1). --define(COL_BUCK, ?COL_OWNER+1). --define(COL_OBJ, ?COL_BUCK+1). --define(COL_MEM, ?COL_OBJ+1). --define(COL_TYPE, ?COL_MEM+1). - -%% Callbacks for cdv_virtual_list -col_to_elem(id) -> col_to_elem(?COL_ID); -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_BUCK) -> #ets_table.buckets; -col_to_elem(?COL_OBJ) -> #ets_table.size; -col_to_elem(?COL_MEM) -> #ets_table.memory. - -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} - ]. - -get_info(Owner) -> - {ok,Info,TW} = crashdump_viewer:ets_tables(Owner), - {Info,TW}. - -get_detail_cols(all) -> - {[?COL_OWNER],false}; -get_detail_cols(_) -> - {[],false}. diff --git a/lib/observer/src/cdv_fun_cb.erl b/lib/observer/src/cdv_fun_cb.erl new file mode 100644 index 0000000000..689ef0e3bb --- /dev/null +++ b/lib/observer/src/cdv_fun_cb.erl @@ -0,0 +1,58 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_fun_cb). + +-export([col_to_elem/1, + col_spec/0, + get_info/1, + get_detail_cols/1]). + +-include_lib("wx/include/wx.hrl"). +-include("crashdump_viewer.hrl"). + +%% Defines +-define(COL_MOD, 0). +-define(COL_UNIQ, ?COL_MOD+1). +-define(COL_INDEX, ?COL_UNIQ+1). +-define(COL_ADDR, ?COL_INDEX+1). +-define(COL_NADDR, ?COL_ADDR+1). +-define(COL_REFC, ?COL_NADDR+1). + +%% Callbacks for cdv_virtual_list_wx +col_to_elem(id) -> col_to_elem(?COL_MOD); +col_to_elem(?COL_MOD) -> #fu.module; +col_to_elem(?COL_UNIQ) -> #fu.uniq; +col_to_elem(?COL_INDEX) -> #fu.index; +col_to_elem(?COL_ADDR) -> #fu.address; +col_to_elem(?COL_NADDR) -> #fu.native_address; +col_to_elem(?COL_REFC) -> #fu.refc. + +col_spec() -> + [{"Module", ?wxLIST_FORMAT_LEFT, 200}, + {"Uniq", ?wxLIST_FORMAT_RIGHT, 100}, + {"Index", ?wxLIST_FORMAT_RIGHT, 50}, + {"Address", ?wxLIST_FORMAT_LEFT, 120}, + {"Native Address", ?wxLIST_FORMAT_LEFT, 120}, + {"Refc", ?wxLIST_FORMAT_RIGHT, 50}]. + +get_info(_) -> + {ok,Info,TW} = crashdump_viewer:funs(), + {Info,TW}. + +get_detail_cols(_) -> + {[?COL_MOD],false}. diff --git a/lib/observer/src/cdv_fun_wx.erl b/lib/observer/src/cdv_fun_wx.erl deleted file mode 100644 index 296abd2719..0000000000 --- a/lib/observer/src/cdv_fun_wx.erl +++ /dev/null @@ -1,58 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_fun_wx). - --export([col_to_elem/1, - col_spec/0, - get_info/1, - get_detail_cols/1]). - --include_lib("wx/include/wx.hrl"). --include("crashdump_viewer.hrl"). - -%% Defines --define(COL_MOD, 0). --define(COL_UNIQ, ?COL_MOD+1). --define(COL_INDEX, ?COL_UNIQ+1). --define(COL_ADDR, ?COL_INDEX+1). --define(COL_NADDR, ?COL_ADDR+1). --define(COL_REFC, ?COL_NADDR+1). - -%% Callbacks for cdv_virtual_list -col_to_elem(id) -> col_to_elem(?COL_MOD); -col_to_elem(?COL_MOD) -> #fu.module; -col_to_elem(?COL_UNIQ) -> #fu.uniq; -col_to_elem(?COL_INDEX) -> #fu.index; -col_to_elem(?COL_ADDR) -> #fu.address; -col_to_elem(?COL_NADDR) -> #fu.native_address; -col_to_elem(?COL_REFC) -> #fu.refc. - -col_spec() -> - [{"Module", ?wxLIST_FORMAT_LEFT, 200}, - {"Uniq", ?wxLIST_FORMAT_RIGHT, 100}, - {"Index", ?wxLIST_FORMAT_RIGHT, 50}, - {"Address", ?wxLIST_FORMAT_LEFT, 120}, - {"Native Address", ?wxLIST_FORMAT_LEFT, 120}, - {"Refc", ?wxLIST_FORMAT_RIGHT, 50}]. - -get_info(_) -> - {ok,Info,TW} = crashdump_viewer:funs(), - {Info,TW}. - -get_detail_cols(_) -> - {[?COL_MOD],false}. diff --git a/lib/observer/src/cdv_gen_cb.erl b/lib/observer/src/cdv_gen_cb.erl new file mode 100644 index 0000000000..6be717d76d --- /dev/null +++ b/lib/observer/src/cdv_gen_cb.erl @@ -0,0 +1,45 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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_gen_cb). + +-export([get_info/0]). + +-include("crashdump_viewer.hrl"). + +get_info() -> + {ok,Info,TW} = crashdump_viewer:general_info(), + Fields = info_fields(), + Proplist = + crashdump_viewer:to_proplist(record_info(fields,general_info),Info), + {Fields,Proplist,TW}. + +info_fields() -> + [{"General Information", + [{"Slogan",slogan}, + {"Node name",node_name}, + {"Crashdump created on",created}, + {"System version",system_vsn}, + {"Compiled",compile_time}, + {"Taints",taints}, + {"Memory allocated",{bytes,mem_tot}}, + {"Memory maximum",{bytes,mem_max}}, + {"Atoms",num_atoms}, + {"Processes",num_procs}, + {"ETS tables",num_ets}, + {"Timers",num_timers}, + {"Funs",num_fun}]}]. diff --git a/lib/observer/src/cdv_gen_wx.erl b/lib/observer/src/cdv_gen_wx.erl deleted file mode 100644 index 92759aaa6d..0000000000 --- a/lib/observer/src/cdv_gen_wx.erl +++ /dev/null @@ -1,45 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% 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 -%% 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_gen_wx). - --export([get_info/0]). - --include("crashdump_viewer.hrl"). - -get_info() -> - {ok,Info,TW} = crashdump_viewer:general_info(), - Fields = info_fields(), - Proplist = - crashdump_viewer:to_proplist(record_info(fields,general_info),Info), - {Fields,Proplist,TW}. - -info_fields() -> - [{"General Information", - [{"Slogan",slogan}, - {"Node name",node_name}, - {"Crashdump created on",created}, - {"System version",system_vsn}, - {"Compiled",compile_time}, - {"Taints",taints}, - {"Memory allocated",{bytes,mem_tot}}, - {"Memory maximum",{bytes,mem_max}}, - {"Atoms",num_atoms}, - {"Processes",num_procs}, - {"ETS tables",num_ets}, - {"Timers",num_timers}, - {"Funs",num_fun}]}]. diff --git a/lib/observer/src/cdv_html_page.erl b/lib/observer/src/cdv_html_page.erl deleted file mode 100644 index 0208d1d67a..0000000000 --- a/lib/observer/src/cdv_html_page.erl +++ /dev/null @@ -1,128 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_html_page). - --behaviour(wx_object). - --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_cast/2]). - --include_lib("wx/include/wx.hrl"). --include("observer_defs.hrl"). - -%% Records --record(state, - {panel, - expand_table, - expand_wins=[]}). - -start_link(ParentWin, Info) -> - wx_object:start_link(?MODULE, [ParentWin, Info], []). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -init([ParentWin, Fun]) when is_function(Fun) -> - init([ParentWin, Fun()]); -init([ParentWin, {expand,HtmlText,Tab}]) -> - HtmlWin = observer_lib:html_window(ParentWin), - wxHtmlWindow:setPage(HtmlWin,HtmlText), - {HtmlWin, #state{panel=HtmlWin,expand_table=Tab}}; -init([ParentWin, HtmlText]) -> - HtmlWin = observer_lib:html_window(ParentWin), - wxHtmlWindow:setPage(HtmlWin,HtmlText), - {HtmlWin, #state{panel=HtmlWin}}. - -%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -handle_info(active, State) -> - {noreply, State}; - -handle_info(Info, State) -> - io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]), - {noreply, State}. - -terminate(_Reason, _State) -> - ok. - -code_change(_, _, State) -> - {ok, State}. - -handle_call(Msg, _From, State) -> - io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]), - {reply, ok, State}. - -handle_cast({detail_win_closed, Id},#state{expand_wins=Opened0}=State) -> - Opened = lists:keydelete(Id, 1, Opened0), - {noreply, State#state{expand_wins=Opened}}; - -handle_cast(Msg, State) -> - io:format("~p~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]), - {noreply, State}. - -handle_event(#wx{event=#wxHtmlLink{type=command_html_link_clicked, - linkInfo=#wxHtmlLinkInfo{href=Target}}}, - #state{expand_table=Tab}=State) -> - NewState= - case Target of - "#Binary?" ++ BinSpec -> - [{"offset",Off},{"size",Size},{"pos",Pos}] = - httpd:parse_query(BinSpec), - Id = {cdv, {list_to_integer(Off), - list_to_integer(Size), - list_to_integer(Pos)}}, - expand(Id,cdv_bin_wx,State); - "#OBSBinary?" ++ BinSpec -> - [{"key1",Preview},{"key2",Size},{"key3",Hash}] = - httpd:parse_query(BinSpec), - Id = {obs, {Tab, {list_to_integer(Preview), - list_to_integer(Size), - list_to_integer(Hash)}}}, - expand(Id,cdv_bin_wx,State); - "#Term?" ++ TermKeys -> - [{"key1",Key1},{"key2",Key2},{"key3",Key3}] = - httpd:parse_query(TermKeys), - Id = {cdv, {Tab,{list_to_integer(Key1), - list_to_integer(Key2), - list_to_integer(Key3)}}}, - expand(Id,cdv_term_wx,State); - _ -> - cdv_virtual_list:start_detail_win(Target), - State - end, - {noreply, NewState}; - -handle_event(Event, State) -> - io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]), - {noreply, State}. - -%%%----------------------------------------------------------------- -%%% Internal -expand(Id,Callback,#state{expand_wins=Opened0}=State) -> - Opened = - case lists:keyfind(Id,1,Opened0) of - false -> - EW = cdv_detail_win:start_link(Id,State#state.panel,Callback), - wx_object:get_pid(EW) ! active, - [{Id,EW}|Opened0]; - {_,EW} -> - wxFrame:raise(EW), - Opened0 - end, - State#state{expand_wins=Opened}. diff --git a/lib/observer/src/cdv_html_wx.erl b/lib/observer/src/cdv_html_wx.erl new file mode 100644 index 0000000000..fe77a41f59 --- /dev/null +++ b/lib/observer/src/cdv_html_wx.erl @@ -0,0 +1,128 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_html_wx). + +-behaviour(wx_object). + +-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_cast/2]). + +-include_lib("wx/include/wx.hrl"). +-include("observer_defs.hrl"). + +%% Records +-record(state, + {panel, + expand_table, + expand_wins=[]}). + +start_link(ParentWin, Info) -> + wx_object:start_link(?MODULE, [ParentWin, Info], []). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([ParentWin, Fun]) when is_function(Fun) -> + init([ParentWin, Fun()]); +init([ParentWin, {expand,HtmlText,Tab}]) -> + HtmlWin = observer_lib:html_window(ParentWin), + wxHtmlWindow:setPage(HtmlWin,HtmlText), + {HtmlWin, #state{panel=HtmlWin,expand_table=Tab}}; +init([ParentWin, HtmlText]) -> + HtmlWin = observer_lib:html_window(ParentWin), + wxHtmlWindow:setPage(HtmlWin,HtmlText), + {HtmlWin, #state{panel=HtmlWin}}. + +%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +handle_info(active, State) -> + {noreply, State}; + +handle_info(Info, State) -> + io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_, _, State) -> + {ok, State}. + +handle_call(Msg, _From, State) -> + io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]), + {reply, ok, State}. + +handle_cast({detail_win_closed, Id},#state{expand_wins=Opened0}=State) -> + Opened = lists:keydelete(Id, 1, Opened0), + {noreply, State#state{expand_wins=Opened}}; + +handle_cast(Msg, State) -> + io:format("~p~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]), + {noreply, State}. + +handle_event(#wx{event=#wxHtmlLink{type=command_html_link_clicked, + linkInfo=#wxHtmlLinkInfo{href=Target}}}, + #state{expand_table=Tab}=State) -> + NewState= + case Target of + "#Binary?" ++ BinSpec -> + [{"offset",Off},{"size",Size},{"pos",Pos}] = + httpd:parse_query(BinSpec), + Id = {cdv, {list_to_integer(Off), + list_to_integer(Size), + list_to_integer(Pos)}}, + expand(Id,cdv_bin_cb,State); + "#OBSBinary?" ++ BinSpec -> + [{"key1",Preview},{"key2",Size},{"key3",Hash}] = + httpd:parse_query(BinSpec), + Id = {obs, {Tab, {list_to_integer(Preview), + list_to_integer(Size), + list_to_integer(Hash)}}}, + expand(Id,cdv_bin_cb,State); + "#Term?" ++ TermKeys -> + [{"key1",Key1},{"key2",Key2},{"key3",Key3}] = + httpd:parse_query(TermKeys), + Id = {cdv, {Tab,{list_to_integer(Key1), + list_to_integer(Key2), + list_to_integer(Key3)}}}, + expand(Id,cdv_term_cb,State); + _ -> + cdv_virtual_list_wx:start_detail_win(Target), + State + end, + {noreply, NewState}; + +handle_event(Event, State) -> + io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]), + {noreply, State}. + +%%%----------------------------------------------------------------- +%%% Internal +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), + wx_object:get_pid(EW) ! active, + [{Id,EW}|Opened0]; + {_,EW} -> + wxFrame:raise(EW), + Opened0 + end, + State#state{expand_wins=Opened}. diff --git a/lib/observer/src/cdv_info_page.erl b/lib/observer/src/cdv_info_page.erl deleted file mode 100644 index 27eb71225d..0000000000 --- a/lib/observer/src/cdv_info_page.erl +++ /dev/null @@ -1,128 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% 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 -%% 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_info_page). - --behaviour(wx_object). - --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_cast/2]). - --include_lib("wx/include/wx.hrl"). --include("observer_defs.hrl"). - -%% Records --record(state, - {panel, - sizer, - fpanel, - callback, - trunc_warn=[] - }). - -start_link(ParentWin, Info) -> - wx_object:start_link(?MODULE, [ParentWin, Info], []). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -init([ParentWin, Callback]) when is_atom(Callback) -> - {InfoFields,Info,TW} = Callback:get_info(), - {Panel,Sizer,FPanel} = create_box(ParentWin,InfoFields,Info), - {Panel,#state{panel=Panel, - sizer=Sizer, - fpanel=FPanel, - callback=Callback, - trunc_warn=TW}}; - -init([ParentWin, {InfoFields,Info,TW}]) -> - {Panel,Sizer,FPanel} = create_box(ParentWin,InfoFields,Info), - {Panel, #state{panel=Panel, - sizer=Sizer, - fpanel=FPanel, - trunc_warn=TW}}. - -%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -handle_info(active, State) -> - crashdump_viewer_wx:set_status(State#state.trunc_warn), - {noreply, State}; - -handle_info(Info, State) -> - io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]), - {noreply, State}. - -terminate(_Reason, _State) -> - ok. - -code_change(_, _, State) -> - {ok, State}. - -handle_call(new_dump, _From, #state{callback=Callback,panel=Panel, - sizer=Sizer,fpanel=FPanel} = State) -> - {InfoFields,Info,TW} = Callback:get_info(), - NewFPanel = - wx:batch( - fun() -> - wxWindow:destroy(FPanel), - FP = create_field_panel(Panel,Sizer,InfoFields,Info), - wxSizer:layout(Sizer), - FP - end), - {reply, ok, State#state{fpanel=NewFPanel,trunc_warn=TW}}; - -handle_call(Msg, _From, State) -> - io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]), - {reply, ok, State}. - -handle_cast(Msg, State) -> - io:format("~p~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]), - {noreply, State}. - -handle_event(#wx{event=#wxMouse{type=left_down},userData=Target}, State) -> - cdv_virtual_list:start_detail_win(Target), - {noreply, State}; - -handle_event(#wx{obj=Obj,event=#wxMouse{type=enter_window}},State) -> - wxTextCtrl:setForegroundColour(Obj,{0,0,100,255}), - {noreply, State}; - -handle_event(#wx{obj=Obj,event=#wxMouse{type=leave_window}},State) -> - wxTextCtrl:setForegroundColour(Obj,?wxBLUE), - {noreply, State}; - -handle_event(Event, State) -> - io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]), - {noreply, State}. - -%%%----------------------------------------------------------------- -%%% Internal -create_box(ParentWin,InfoFields,Info) -> - Panel = wxPanel:new(ParentWin), - Sizer = wxBoxSizer:new(?wxVERTICAL), - FPanel = create_field_panel(Panel,Sizer,InfoFields,Info), - wxPanel:setSizer(Panel, Sizer), - {Panel,Sizer,FPanel}. - -create_field_panel(Panel,Sizer,InfoFields,Info0) -> - Info = observer_lib:fill_info(InfoFields, Info0), - {FPanel, _FSizer, _Fields} = observer_lib:display_info(Panel,Info), - BorderFlags = ?wxLEFT bor ?wxRIGHT, - wxSizer:add(Sizer, FPanel, [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP}, - {proportion, 0}, {border, 5}]), - FPanel. diff --git a/lib/observer/src/cdv_info_wx.erl b/lib/observer/src/cdv_info_wx.erl new file mode 100644 index 0000000000..59ce0cabb1 --- /dev/null +++ b/lib/observer/src/cdv_info_wx.erl @@ -0,0 +1,128 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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_info_wx). + +-behaviour(wx_object). + +-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_cast/2]). + +-include_lib("wx/include/wx.hrl"). +-include("observer_defs.hrl"). + +%% Records +-record(state, + {panel, + sizer, + fpanel, + callback, + trunc_warn=[] + }). + +start_link(ParentWin, Info) -> + wx_object:start_link(?MODULE, [ParentWin, Info], []). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([ParentWin, Callback]) when is_atom(Callback) -> + {InfoFields,Info,TW} = Callback:get_info(), + {Panel,Sizer,FPanel} = create_box(ParentWin,InfoFields,Info), + {Panel,#state{panel=Panel, + sizer=Sizer, + fpanel=FPanel, + callback=Callback, + trunc_warn=TW}}; + +init([ParentWin, {InfoFields,Info,TW}]) -> + {Panel,Sizer,FPanel} = create_box(ParentWin,InfoFields,Info), + {Panel, #state{panel=Panel, + sizer=Sizer, + fpanel=FPanel, + trunc_warn=TW}}. + +%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +handle_info(active, State) -> + cdv_wx:set_status(State#state.trunc_warn), + {noreply, State}; + +handle_info(Info, State) -> + io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_, _, State) -> + {ok, State}. + +handle_call(new_dump, _From, #state{callback=Callback,panel=Panel, + sizer=Sizer,fpanel=FPanel} = State) -> + {InfoFields,Info,TW} = Callback:get_info(), + NewFPanel = + wx:batch( + fun() -> + wxWindow:destroy(FPanel), + FP = create_field_panel(Panel,Sizer,InfoFields,Info), + wxSizer:layout(Sizer), + FP + end), + {reply, ok, State#state{fpanel=NewFPanel,trunc_warn=TW}}; + +handle_call(Msg, _From, State) -> + io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]), + {reply, ok, State}. + +handle_cast(Msg, State) -> + io:format("~p~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]), + {noreply, State}. + +handle_event(#wx{event=#wxMouse{type=left_down},userData=Target}, State) -> + cdv_virtual_list_wx:start_detail_win(Target), + {noreply, State}; + +handle_event(#wx{obj=Obj,event=#wxMouse{type=enter_window}},State) -> + wxTextCtrl:setForegroundColour(Obj,{0,0,100,255}), + {noreply, State}; + +handle_event(#wx{obj=Obj,event=#wxMouse{type=leave_window}},State) -> + wxTextCtrl:setForegroundColour(Obj,?wxBLUE), + {noreply, State}; + +handle_event(Event, State) -> + io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]), + {noreply, State}. + +%%%----------------------------------------------------------------- +%%% Internal +create_box(ParentWin,InfoFields,Info) -> + Panel = wxPanel:new(ParentWin), + Sizer = wxBoxSizer:new(?wxVERTICAL), + FPanel = create_field_panel(Panel,Sizer,InfoFields,Info), + wxPanel:setSizer(Panel, Sizer), + {Panel,Sizer,FPanel}. + +create_field_panel(Panel,Sizer,InfoFields,Info0) -> + Info = observer_lib:fill_info(InfoFields, Info0), + {FPanel, _FSizer, _Fields} = observer_lib:display_info(Panel,Info), + BorderFlags = ?wxLEFT bor ?wxRIGHT, + wxSizer:add(Sizer, FPanel, [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP}, + {proportion, 0}, {border, 5}]), + FPanel. diff --git a/lib/observer/src/cdv_int_tab_cb.erl b/lib/observer/src/cdv_int_tab_cb.erl new file mode 100644 index 0000000000..31727391fe --- /dev/null +++ b/lib/observer/src/cdv_int_tab_cb.erl @@ -0,0 +1,86 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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_int_tab_cb). + +-export([get_info/0]). + +-include_lib("wx/include/wx.hrl"). +-include("crashdump_viewer.hrl"). + +get_info() -> + observer_lib:report_progress({ok,"Processing internal tables"}), + HashInfo = get_hash_info(), + observer_lib:report_progress({ok,33}), + IndexInfo = get_index_info(), + observer_lib:report_progress({ok,66}), + IntEtsInfo = get_internal_ets_info(), + observer_lib:report_progress({ok,100}), + [{"Hash Tables",cdv_table_wx,HashInfo}, + {"Index Tables",cdv_table_wx,IndexInfo}, + {"Internal ETS Tables",cdv_table_wx,IntEtsInfo}]. + +%%%----------------------------------------------------------------- +%%% Hash tables +get_hash_info() -> + {ok,Info0,TW} = crashdump_viewer:hash_tables(), + Columns = hash_columns(), + Info = [crashdump_viewer:to_value_list(R) || R <- Info0], + {Columns,Info,TW}. + +hash_columns() -> + [{"Name", ?wxLIST_FORMAT_LEFT, 150}, + {"Size", ?wxLIST_FORMAT_RIGHT, 100}, + {"Used", ?wxLIST_FORMAT_RIGHT, 100}, + {"Objects",?wxLIST_FORMAT_RIGHT, 100}, + {"Depth", ?wxLIST_FORMAT_RIGHT, 100}]. + +%%%----------------------------------------------------------------- +%%% Index tables +get_index_info() -> + {ok,Info0,TW} = crashdump_viewer:index_tables(), + Columns = index_columns(), + Info = [crashdump_viewer:to_value_list(R) || R <- Info0], + {Columns,Info,TW}. + +index_columns() -> + [{"Name", ?wxLIST_FORMAT_LEFT, 150}, + {"Size", ?wxLIST_FORMAT_RIGHT, 100}, + {"Limit", ?wxLIST_FORMAT_RIGHT, 100}, + {"Used", ?wxLIST_FORMAT_RIGHT, 100}, + {"Rate", ?wxLIST_FORMAT_RIGHT, 100}, + {"Entries",?wxLIST_FORMAT_RIGHT, 100}]. + +%%%----------------------------------------------------------------- +%%% Internal ets tables +get_internal_ets_info() -> + {ok,Info0,TW} = crashdump_viewer:internal_ets_tables(), + Columns = int_ets_columns(), + Info = [begin + [_,_|Data] = crashdump_viewer:to_value_list(R), %skip pid and slot + [Desc|Data] + end || {Desc,R} <- Info0], + {Columns,Info,TW}. + +int_ets_columns() -> + [{"Description", ?wxLIST_FORMAT_LEFT, 170}, + {"Id", ?wxLIST_FORMAT_LEFT, 80}, + {"Name", ?wxLIST_FORMAT_LEFT, 80}, + {"Type", ?wxLIST_FORMAT_LEFT, 80}, + {"Buckets", ?wxLIST_FORMAT_RIGHT, 80}, + {"Objects", ?wxLIST_FORMAT_RIGHT, 80}, + {"Memory", ?wxLIST_FORMAT_RIGHT, 80}]. diff --git a/lib/observer/src/cdv_int_tab_wx.erl b/lib/observer/src/cdv_int_tab_wx.erl deleted file mode 100644 index b4fa6ca889..0000000000 --- a/lib/observer/src/cdv_int_tab_wx.erl +++ /dev/null @@ -1,86 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% 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 -%% 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_int_tab_wx). - --export([get_info/0]). - --include_lib("wx/include/wx.hrl"). --include("crashdump_viewer.hrl"). - -get_info() -> - observer_lib:report_progress({ok,"Processing internal tables"}), - HashInfo = get_hash_info(), - observer_lib:report_progress({ok,33}), - IndexInfo = get_index_info(), - observer_lib:report_progress({ok,66}), - IntEtsInfo = get_internal_ets_info(), - observer_lib:report_progress({ok,100}), - [{"Hash Tables",cdv_table_page,HashInfo}, - {"Index Tables",cdv_table_page,IndexInfo}, - {"Internal ETS Tables",cdv_table_page,IntEtsInfo}]. - -%%%----------------------------------------------------------------- -%%% Hash tables -get_hash_info() -> - {ok,Info0,TW} = crashdump_viewer:hash_tables(), - Columns = hash_columns(), - Info = [crashdump_viewer:to_value_list(R) || R <- Info0], - {Columns,Info,TW}. - -hash_columns() -> - [{"Name", ?wxLIST_FORMAT_LEFT, 150}, - {"Size", ?wxLIST_FORMAT_RIGHT, 100}, - {"Used", ?wxLIST_FORMAT_RIGHT, 100}, - {"Objects",?wxLIST_FORMAT_RIGHT, 100}, - {"Depth", ?wxLIST_FORMAT_RIGHT, 100}]. - -%%%----------------------------------------------------------------- -%%% Index tables -get_index_info() -> - {ok,Info0,TW} = crashdump_viewer:index_tables(), - Columns = index_columns(), - Info = [crashdump_viewer:to_value_list(R) || R <- Info0], - {Columns,Info,TW}. - -index_columns() -> - [{"Name", ?wxLIST_FORMAT_LEFT, 150}, - {"Size", ?wxLIST_FORMAT_RIGHT, 100}, - {"Limit", ?wxLIST_FORMAT_RIGHT, 100}, - {"Used", ?wxLIST_FORMAT_RIGHT, 100}, - {"Rate", ?wxLIST_FORMAT_RIGHT, 100}, - {"Entries",?wxLIST_FORMAT_RIGHT, 100}]. - -%%%----------------------------------------------------------------- -%%% Internal ets tables -get_internal_ets_info() -> - {ok,Info0,TW} = crashdump_viewer:internal_ets_tables(), - Columns = int_ets_columns(), - Info = [begin - [_,_|Data] = crashdump_viewer:to_value_list(R), %skip pid and slot - [Desc|Data] - end || {Desc,R} <- Info0], - {Columns,Info,TW}. - -int_ets_columns() -> - [{"Description", ?wxLIST_FORMAT_LEFT, 170}, - {"Id", ?wxLIST_FORMAT_LEFT, 80}, - {"Name", ?wxLIST_FORMAT_LEFT, 80}, - {"Type", ?wxLIST_FORMAT_LEFT, 80}, - {"Buckets", ?wxLIST_FORMAT_RIGHT, 80}, - {"Objects", ?wxLIST_FORMAT_RIGHT, 80}, - {"Memory", ?wxLIST_FORMAT_RIGHT, 80}]. diff --git a/lib/observer/src/cdv_mem_cb.erl b/lib/observer/src/cdv_mem_cb.erl new file mode 100644 index 0000000000..2b0809df13 --- /dev/null +++ b/lib/observer/src/cdv_mem_cb.erl @@ -0,0 +1,84 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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_mem_cb). + +-export([get_info/0]). + +-include("crashdump_viewer.hrl"). +-include_lib("wx/include/wx.hrl"). + +get_info() -> + observer_lib:report_progress({ok,"Processing memory info"}), + MemInfo = get_mem_info(), + observer_lib:report_progress({ok,33}), + {AllocInfo,AllocTW} = get_alloc_info(), + observer_lib:report_progress({ok,66}), + AreaInfo = get_area_info(), + observer_lib:report_progress({ok,100}), + [{"Memory",cdv_info_wx,MemInfo} + | [{Title,cdv_table_wx,{Cols,Data,AllocTW}} || + {Title,Cols,Data} <- AllocInfo]] ++ + [{"Allocated Areas",cdv_table_wx,AreaInfo}]. + + +%%%----------------------------------------------------------------- +%%% Memory page +get_mem_info() -> + {ok,Info,TW} = crashdump_viewer:memory(), + {[{"Memory Information",gen_mem_info_fields(Info)}],Info,TW}. + +gen_mem_info_fields([{Key,_}|T]) -> + [{upper(atom_to_list(Key)),{bytes,Key}}|gen_mem_info_fields(T)]; +gen_mem_info_fields([]) -> + []. + +upper(Key) -> + string:join([string:to_upper([H]) ++ T || + [H|T] <- string:tokens(Key,"_")]," "). + + +%%%----------------------------------------------------------------- +%%% Allocated areas page +get_area_info() -> + {ok,Info0,TW} = crashdump_viewer:allocated_areas(), + Info = [tuple_to_list(R) || R <- Info0], + {area_columns(),Info,TW}. + +area_columns() -> + [{"", ?wxLIST_FORMAT_LEFT, 150}, + {"Allocated (bytes)",?wxLIST_FORMAT_RIGHT, 150}, + {"Used (bytes)", ?wxLIST_FORMAT_RIGHT, 150}]. + +%%%----------------------------------------------------------------- +%%% Allocator page +get_alloc_info() -> + {ok,Info,TW} = crashdump_viewer:allocator_info(), + {fix_alloc(Info),TW}. + +fix_alloc([{Title,Columns,Data}|Tables]) -> + [{Title,alloc_columns(Columns), + [[Key|Values] || {Key,Values} <- Data]} | + fix_alloc(Tables)]; +fix_alloc([{Title,[{_,V}|_]=Data}|Tables]) -> + fix_alloc([{Title,lists:duplicate(length(V),[]),Data}|Tables]); +fix_alloc([]) -> + []. + +alloc_columns(Columns) -> + [{"", ?wxLIST_FORMAT_LEFT, 180} | + [{Column, ?wxLIST_FORMAT_RIGHT, 140} || Column <- Columns]]. diff --git a/lib/observer/src/cdv_mem_wx.erl b/lib/observer/src/cdv_mem_wx.erl deleted file mode 100644 index 673795f39d..0000000000 --- a/lib/observer/src/cdv_mem_wx.erl +++ /dev/null @@ -1,84 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% 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 -%% 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_mem_wx). - --export([get_info/0]). - --include("crashdump_viewer.hrl"). --include_lib("wx/include/wx.hrl"). - -get_info() -> - observer_lib:report_progress({ok,"Processing memory info"}), - MemInfo = get_mem_info(), - observer_lib:report_progress({ok,33}), - {AllocInfo,AllocTW} = get_alloc_info(), - observer_lib:report_progress({ok,66}), - AreaInfo = get_area_info(), - observer_lib:report_progress({ok,100}), - [{"Memory",cdv_info_page,MemInfo} - | [{Title,cdv_table_page,{Cols,Data,AllocTW}} || - {Title,Cols,Data} <- AllocInfo]] ++ - [{"Allocated Areas",cdv_table_page,AreaInfo}]. - - -%%%----------------------------------------------------------------- -%%% Memory page -get_mem_info() -> - {ok,Info,TW} = crashdump_viewer:memory(), - {[{"Memory Information",gen_mem_info_fields(Info)}],Info,TW}. - -gen_mem_info_fields([{Key,_}|T]) -> - [{upper(atom_to_list(Key)),{bytes,Key}}|gen_mem_info_fields(T)]; -gen_mem_info_fields([]) -> - []. - -upper(Key) -> - string:join([string:to_upper([H]) ++ T || - [H|T] <- string:tokens(Key,"_")]," "). - - -%%%----------------------------------------------------------------- -%%% Allocated areas page -get_area_info() -> - {ok,Info0,TW} = crashdump_viewer:allocated_areas(), - Info = [tuple_to_list(R) || R <- Info0], - {area_columns(),Info,TW}. - -area_columns() -> - [{"", ?wxLIST_FORMAT_LEFT, 150}, - {"Allocated (bytes)",?wxLIST_FORMAT_RIGHT, 150}, - {"Used (bytes)", ?wxLIST_FORMAT_RIGHT, 150}]. - -%%%----------------------------------------------------------------- -%%% Allocator page -get_alloc_info() -> - {ok,Info,TW} = crashdump_viewer:allocator_info(), - {fix_alloc(Info),TW}. - -fix_alloc([{Title,Columns,Data}|Tables]) -> - [{Title,alloc_columns(Columns), - [[Key|Values] || {Key,Values} <- Data]} | - fix_alloc(Tables)]; -fix_alloc([{Title,[{_,V}|_]=Data}|Tables]) -> - fix_alloc([{Title,lists:duplicate(length(V),[]),Data}|Tables]); -fix_alloc([]) -> - []. - -alloc_columns(Columns) -> - [{"", ?wxLIST_FORMAT_LEFT, 180} | - [{Column, ?wxLIST_FORMAT_RIGHT, 140} || Column <- Columns]]. diff --git a/lib/observer/src/cdv_mod_cb.erl b/lib/observer/src/cdv_mod_cb.erl new file mode 100644 index 0000000000..e829ff4fca --- /dev/null +++ b/lib/observer/src/cdv_mod_cb.erl @@ -0,0 +1,102 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_mod_cb). + +-export([col_to_elem/1, + col_spec/0, + get_info/1, + get_detail_cols/1, + get_details/1, + detail_pages/0, + format/1]). + +-include_lib("wx/include/wx.hrl"). +-include("crashdump_viewer.hrl"). + +%% Defines +-define(COL_ID, 0). +-define(COL_CUR, ?COL_ID+1). +-define(COL_OLD, ?COL_CUR+1). + +%% Callbacks for cdv_virtual_list_wx +col_to_elem(id) -> col_to_elem(?COL_ID); +col_to_elem(?COL_ID) -> #loaded_mod.mod; +col_to_elem(?COL_CUR) -> #loaded_mod.current_size; +col_to_elem(?COL_OLD) -> #loaded_mod.old_size. + +col_spec() -> + [{"Module", ?wxLIST_FORMAT_LEFT, 300}, + {"Current size", ?wxLIST_FORMAT_RIGHT, 80}, + {"Old size", ?wxLIST_FORMAT_RIGHT, 80}]. + +get_info(_) -> + {ok,Info,TW} = crashdump_viewer:loaded_modules(), + {Info,TW}. + +get_detail_cols(_) -> + {[?COL_ID],true}. + +%% Callbacks for cdv_detail_wx +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]), + {ok,{Title,Proplist,TW}}. + +detail_pages() -> + [{"General Information", fun init_gen_page/2}, + {"Current Attributes", fun init_curr_attr_page/2}, + {"Current Compilation Info", fun init_curr_comp_page/2}, + {"Old Attributes", fun init_old_attr_page/2}, + {"Old Compilation Info", fun init_old_comp_page/2}]. + +init_gen_page(Parent, Info) -> + Fields = info_fields(), + cdv_info_wx:start_link(Parent,{Fields,Info,[]}). + +init_curr_attr_page(Parent, Info) -> + init_info_page(Parent, proplists:get_value(current_attrib,Info)). + +init_curr_comp_page(Parent, Info) -> + init_info_page(Parent, proplists:get_value(current_comp_info,Info)). + +init_old_attr_page(Parent, Info) -> + init_info_page(Parent, proplists:get_value(old_attrib,Info)). + +init_old_comp_page(Parent, Info) -> + init_info_page(Parent, proplists:get_value(old_comp_info,Info)). + +init_info_page(Parent, undefined) -> + init_info_page(Parent, ""); +init_info_page(Parent, String) -> + cdv_html_wx:start_link(Parent,observer_html_lib:plain_page(String)). + +format({Bin,q}) when is_binary(Bin) -> + [$'|binary_to_list(Bin)]; +format({Bin,nq}) when is_binary(Bin) -> + lists:flatten(io_lib:format("~ts",[Bin])); +format(D) -> + D. + +%%%----------------------------------------------------------------- +%%% Internal +info_fields() -> + [{"Overview", + [{"Name", mod}, + {"Current Size", current_size}, + {"Old Size", old_size}]}]. diff --git a/lib/observer/src/cdv_mod_wx.erl b/lib/observer/src/cdv_mod_wx.erl deleted file mode 100644 index 8751651fec..0000000000 --- a/lib/observer/src/cdv_mod_wx.erl +++ /dev/null @@ -1,102 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_mod_wx). - --export([col_to_elem/1, - col_spec/0, - get_info/1, - get_detail_cols/1, - get_details/1, - detail_pages/0, - format/1]). - --include_lib("wx/include/wx.hrl"). --include("crashdump_viewer.hrl"). - -%% Defines --define(COL_ID, 0). --define(COL_CUR, ?COL_ID+1). --define(COL_OLD, ?COL_CUR+1). - -%% Callbacks for cdv_virtual_list -col_to_elem(id) -> col_to_elem(?COL_ID); -col_to_elem(?COL_ID) -> #loaded_mod.mod; -col_to_elem(?COL_CUR) -> #loaded_mod.current_size; -col_to_elem(?COL_OLD) -> #loaded_mod.old_size. - -col_spec() -> - [{"Module", ?wxLIST_FORMAT_LEFT, 300}, - {"Current size", ?wxLIST_FORMAT_RIGHT, 80}, - {"Old size", ?wxLIST_FORMAT_RIGHT, 80}]. - -get_info(_) -> - {ok,Info,TW} = crashdump_viewer:loaded_modules(), - {Info,TW}. - -get_detail_cols(_) -> - {[?COL_ID],true}. - -%% Callbacks for cdv_detail_win -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]), - {ok,{Title,Proplist,TW}}. - -detail_pages() -> - [{"General Information", fun init_gen_page/2}, - {"Current Attributes", fun init_curr_attr_page/2}, - {"Current Compilation Info", fun init_curr_comp_page/2}, - {"Old Attributes", fun init_old_attr_page/2}, - {"Old Compilation Info", fun init_old_comp_page/2}]. - -init_gen_page(Parent, Info) -> - Fields = info_fields(), - cdv_info_page:start_link(Parent,{Fields,Info,[]}). - -init_curr_attr_page(Parent, Info) -> - init_info_page(Parent, proplists:get_value(current_attrib,Info)). - -init_curr_comp_page(Parent, Info) -> - init_info_page(Parent, proplists:get_value(current_comp_info,Info)). - -init_old_attr_page(Parent, Info) -> - init_info_page(Parent, proplists:get_value(old_attrib,Info)). - -init_old_comp_page(Parent, Info) -> - init_info_page(Parent, proplists:get_value(old_comp_info,Info)). - -init_info_page(Parent, undefined) -> - init_info_page(Parent, ""); -init_info_page(Parent, String) -> - cdv_html_page:start_link(Parent,crashdump_viewer_html:plain_page(String)). - -format({Bin,q}) when is_binary(Bin) -> - [$'|binary_to_list(Bin)]; -format({Bin,nq}) when is_binary(Bin) -> - lists:flatten(io_lib:format("~ts",[Bin])); -format(D) -> - D. - -%%%----------------------------------------------------------------- -%%% Internal -info_fields() -> - [{"Overview", - [{"Name", mod}, - {"Current Size", current_size}, - {"Old Size", old_size}]}]. diff --git a/lib/observer/src/cdv_multi_panel.erl b/lib/observer/src/cdv_multi_panel.erl deleted file mode 100644 index 8218824ff2..0000000000 --- a/lib/observer/src/cdv_multi_panel.erl +++ /dev/null @@ -1,188 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% 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 -%% 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_multi_panel). - --behaviour(wx_object). - --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_cast/2]). - --include_lib("wx/include/wx.hrl"). --include("observer_defs.hrl"). - -%% Records --record(state, - {main_panel, - main_sizer, - menu, - menu_sizer, - callback, - pages, - dyn_panel, - dyn_sizer, - dyn_page - }). - -start_link(Notebook, Info) -> - wx_object:start_link(?MODULE, [Notebook, Info], []). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -init([Notebook, Callback]) when is_atom(Callback) -> - Pages = Callback:get_info(), - {MainPanel,State0} = init([Notebook, Pages]), - {MainPanel,State0#state{callback=Callback}}; -init([Notebook, Pages]) -> - MainPanel = wxPanel:new(Notebook), - Sizer = wxBoxSizer:new(?wxHORIZONTAL), - LeftMenuSizer = wxStaticBoxSizer:new(?wxVERTICAL,MainPanel, - [{label,"Please select"}]), - LeftMenu = wxListBox:new(MainPanel,?wxID_ANY, - [{style,?wxLB_SINGLE}, - {choices,[T || {T,_,_} <- Pages]}]), - wxListBox:setSelection(LeftMenu,0), - wxListBox:connect(LeftMenu, command_listbox_selected), - wxSizer:add(LeftMenuSizer,LeftMenu,[{flag,?wxEXPAND},{proportion,2}]), - - DynPanel = wxScrolledWindow:new(MainPanel), - wxScrolledWindow:enableScrolling(DynPanel,true,true), - wxScrolledWindow:setScrollbars(DynPanel,1,1,0,0), - - BorderFlags = ?wxLEFT bor ?wxRIGHT, - wxSizer:add(Sizer, LeftMenuSizer, - [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP}, - {proportion, 0}, {border, 5}]), - wxSizer:add(Sizer, DynPanel, [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP}, - {proportion, 1}, {border, 5}]), - wxPanel:setSizer(MainPanel, Sizer), - - State = load_dyn_page(#state{main_panel=MainPanel, - main_sizer=Sizer, - menu=LeftMenu, - menu_sizer=LeftMenuSizer, - pages=Pages, - dyn_panel=DynPanel - }), - {MainPanel, State}. - -%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -handle_info(active, State) -> - NewState = - wx:batch( - fun() -> - update_dyn_page(State) - end), - {noreply, NewState}; - -handle_info(Info, State) -> - io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]), - {noreply, State}. - -terminate(_Reason, _State) -> - ok. - -code_change(_, _, State) -> - {ok, State}. - -handle_call(new_dump, _From, State) -> - NewState = - wx:batch( - fun() -> - update_left_menu(State) - end), - {reply, ok, NewState}; - -handle_call(Msg, _From, State) -> - io:format("~p:~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]), - {reply, ok, State}. - -handle_cast(Msg, State) -> - io:format("~p:~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]), - {noreply, State}. - -handle_event(#wx{event=#wxCommand{type=command_listbox_selected, - cmdString=[]}}, - State) -> - %% For some reason, the listbox sometimes gets an "unselect" - %% command like this during termination. Ignore! - {noreply, State}; - -handle_event(#wx{event=#wxCommand{type=command_listbox_selected, - cmdString=_DynName}}, - State) -> - NewState = - wx:batch(fun() -> - update_dyn_page(State) - end), - {noreply,NewState}; - -handle_event(Event, State) -> - io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]), - {noreply, State}. - -%%%%%%%%%%%%%%%%%%%%%%% Internal %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -update_left_menu(#state{main_panel=Panel, - callback=Callback, - menu=OldMenu, - menu_sizer=MenuSizer} = State) -> - Pages = Callback:get_info(), - wxListBox:disconnect(OldMenu), - wxWindow:destroy(OldMenu), - NewMenu = wxListBox:new(Panel,?wxID_ANY, - [{style,?wxLB_SINGLE}, - {choices,[T || {T,_,_} <- Pages]}]), - wxListBox:setSelection(NewMenu,0), - wxListBox:connect(NewMenu, command_listbox_selected), - wxSizer:add(MenuSizer,NewMenu,[{flag,?wxEXPAND},{proportion,2}]), - wxSizer:layout(MenuSizer), - State#state{pages=Pages,menu=NewMenu}. - -update_dyn_page(#state{dyn_page=undefined} = State) -> - load_dyn_page(State); -update_dyn_page(#state{dyn_page=OldDynPage, - dyn_sizer=OldDynSizer} = State) -> - wxSizer:detach(OldDynSizer,OldDynPage), - wxWindow:destroy(OldDynPage), - load_dyn_page(State). - -load_dyn_page(#state{main_sizer=MainSizer, - dyn_panel=DynPanel, - menu=Menu, - pages=Pages} = State) -> - %% Freeze and thaw causes a hang (and is not needed) on 2.9 and higher - DoFreeze = [?wxMAJOR_VERSION,?wxMINOR_VERSION] < [2,9], - DoFreeze andalso wxWindow:freeze(DynPanel), - Name = wxListBox:getStringSelection(Menu), - {Page,Sizer} = load_dyn_page(DynPanel,Name,Pages), - wxSizer:layout(MainSizer), - DoFreeze andalso wxWindow:thaw(DynPanel), - wx_object:get_pid(Page) ! active, - State#state{dyn_page=Page,dyn_sizer=Sizer}. - -load_dyn_page(Panel,Name,Pages) -> - Sizer = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label,Name}]), - - {_,Callback,Info} = lists:keyfind(Name,1,Pages), - DynPage = Callback:start_link(Panel,Info), - - wxSizer:add(Sizer,DynPage,[{flag, ?wxEXPAND}, {proportion, 1}]), - wxPanel:setSizerAndFit(Panel,Sizer,[{deleteOld,true}]), - {DynPage,Sizer}. diff --git a/lib/observer/src/cdv_multi_wx.erl b/lib/observer/src/cdv_multi_wx.erl new file mode 100644 index 0000000000..75c7f48fc2 --- /dev/null +++ b/lib/observer/src/cdv_multi_wx.erl @@ -0,0 +1,188 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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_multi_wx). + +-behaviour(wx_object). + +-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_cast/2]). + +-include_lib("wx/include/wx.hrl"). +-include("observer_defs.hrl"). + +%% Records +-record(state, + {main_panel, + main_sizer, + menu, + menu_sizer, + callback, + pages, + dyn_panel, + dyn_sizer, + dyn_page + }). + +start_link(Notebook, Info) -> + wx_object:start_link(?MODULE, [Notebook, Info], []). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([Notebook, Callback]) when is_atom(Callback) -> + Pages = Callback:get_info(), + {MainPanel,State0} = init([Notebook, Pages]), + {MainPanel,State0#state{callback=Callback}}; +init([Notebook, Pages]) -> + MainPanel = wxPanel:new(Notebook), + Sizer = wxBoxSizer:new(?wxHORIZONTAL), + LeftMenuSizer = wxStaticBoxSizer:new(?wxVERTICAL,MainPanel, + [{label,"Please select"}]), + LeftMenu = wxListBox:new(MainPanel,?wxID_ANY, + [{style,?wxLB_SINGLE}, + {choices,[T || {T,_,_} <- Pages]}]), + wxListBox:setSelection(LeftMenu,0), + wxListBox:connect(LeftMenu, command_listbox_selected), + wxSizer:add(LeftMenuSizer,LeftMenu,[{flag,?wxEXPAND},{proportion,2}]), + + DynPanel = wxScrolledWindow:new(MainPanel), + wxScrolledWindow:enableScrolling(DynPanel,true,true), + wxScrolledWindow:setScrollbars(DynPanel,1,1,0,0), + + BorderFlags = ?wxLEFT bor ?wxRIGHT, + wxSizer:add(Sizer, LeftMenuSizer, + [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP}, + {proportion, 0}, {border, 5}]), + wxSizer:add(Sizer, DynPanel, [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP}, + {proportion, 1}, {border, 5}]), + wxPanel:setSizer(MainPanel, Sizer), + + State = load_dyn_page(#state{main_panel=MainPanel, + main_sizer=Sizer, + menu=LeftMenu, + menu_sizer=LeftMenuSizer, + pages=Pages, + dyn_panel=DynPanel + }), + {MainPanel, State}. + +%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +handle_info(active, State) -> + NewState = + wx:batch( + fun() -> + update_dyn_page(State) + end), + {noreply, NewState}; + +handle_info(Info, State) -> + io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_, _, State) -> + {ok, State}. + +handle_call(new_dump, _From, State) -> + NewState = + wx:batch( + fun() -> + update_left_menu(State) + end), + {reply, ok, NewState}; + +handle_call(Msg, _From, State) -> + io:format("~p:~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]), + {reply, ok, State}. + +handle_cast(Msg, State) -> + io:format("~p:~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]), + {noreply, State}. + +handle_event(#wx{event=#wxCommand{type=command_listbox_selected, + cmdString=[]}}, + State) -> + %% For some reason, the listbox sometimes gets an "unselect" + %% command like this during termination. Ignore! + {noreply, State}; + +handle_event(#wx{event=#wxCommand{type=command_listbox_selected, + cmdString=_DynName}}, + State) -> + NewState = + wx:batch(fun() -> + update_dyn_page(State) + end), + {noreply,NewState}; + +handle_event(Event, State) -> + io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]), + {noreply, State}. + +%%%%%%%%%%%%%%%%%%%%%%% Internal %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +update_left_menu(#state{main_panel=Panel, + callback=Callback, + menu=OldMenu, + menu_sizer=MenuSizer} = State) -> + Pages = Callback:get_info(), + wxListBox:disconnect(OldMenu), + wxWindow:destroy(OldMenu), + NewMenu = wxListBox:new(Panel,?wxID_ANY, + [{style,?wxLB_SINGLE}, + {choices,[T || {T,_,_} <- Pages]}]), + wxListBox:setSelection(NewMenu,0), + wxListBox:connect(NewMenu, command_listbox_selected), + wxSizer:add(MenuSizer,NewMenu,[{flag,?wxEXPAND},{proportion,2}]), + wxSizer:layout(MenuSizer), + State#state{pages=Pages,menu=NewMenu}. + +update_dyn_page(#state{dyn_page=undefined} = State) -> + load_dyn_page(State); +update_dyn_page(#state{dyn_page=OldDynPage, + dyn_sizer=OldDynSizer} = State) -> + wxSizer:detach(OldDynSizer,OldDynPage), + wxWindow:destroy(OldDynPage), + load_dyn_page(State). + +load_dyn_page(#state{main_sizer=MainSizer, + dyn_panel=DynPanel, + menu=Menu, + pages=Pages} = State) -> + %% Freeze and thaw causes a hang (and is not needed) on 2.9 and higher + DoFreeze = [?wxMAJOR_VERSION,?wxMINOR_VERSION] < [2,9], + DoFreeze andalso wxWindow:freeze(DynPanel), + Name = wxListBox:getStringSelection(Menu), + {Page,Sizer} = load_dyn_page(DynPanel,Name,Pages), + wxSizer:layout(MainSizer), + DoFreeze andalso wxWindow:thaw(DynPanel), + wx_object:get_pid(Page) ! active, + State#state{dyn_page=Page,dyn_sizer=Sizer}. + +load_dyn_page(Panel,Name,Pages) -> + Sizer = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label,Name}]), + + {_,Callback,Info} = lists:keyfind(Name,1,Pages), + DynPage = Callback:start_link(Panel,Info), + + wxSizer:add(Sizer,DynPage,[{flag, ?wxEXPAND}, {proportion, 1}]), + wxPanel:setSizerAndFit(Panel,Sizer,[{deleteOld,true}]), + {DynPage,Sizer}. diff --git a/lib/observer/src/cdv_port_cb.erl b/lib/observer/src/cdv_port_cb.erl new file mode 100644 index 0000000000..08488d3e34 --- /dev/null +++ b/lib/observer/src/cdv_port_cb.erl @@ -0,0 +1,103 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_port_cb). + +-export([col_to_elem/1, + col_spec/0, + get_info/1, + get_detail_cols/1, + get_details/1, + detail_pages/0, + format/1]). + +-include_lib("wx/include/wx.hrl"). +-include("crashdump_viewer.hrl"). + +%% Columns +-define(COL_ID, 0). +-define(COL_CONN, ?COL_ID+1). +-define(COL_NAME, ?COL_CONN+1). +-define(COL_CTRL, ?COL_NAME+1). +-define(COL_SLOT, ?COL_CTRL+1). + + + +%% Callbacks for cdv_virtual_list_wx +col_to_elem(id) -> col_to_elem(?COL_ID); +col_to_elem(?COL_ID) -> #port.id; +col_to_elem(?COL_CONN) -> #port.connected; +col_to_elem(?COL_NAME) -> #port.name; +col_to_elem(?COL_CTRL) -> #port.controls; +col_to_elem(?COL_SLOT) -> #port.slot. + +col_spec() -> + [{"Id", ?wxLIST_FORMAT_LEFT, 100}, + {"Connected", ?wxLIST_FORMAT_LEFT, 120}, + {"Name", ?wxLIST_FORMAT_LEFT, 150}, + {"Controls", ?wxLIST_FORMAT_LEFT, 200}, + {"Slot", ?wxLIST_FORMAT_RIGHT, 50}]. + +get_info(_) -> + {ok,Info,TW} = crashdump_viewer:ports(), + {Info,TW}. + +get_detail_cols(_) -> + {[?COL_ID,?COL_CONN],true}. + +%% Callbacks for cdv_detail_wx +get_details(Id) -> + case crashdump_viewer:port(Id) of + {ok,Info,TW} -> + Proplist = + crashdump_viewer:to_proplist(record_info(fields,port),Info), + {ok,{Id,Proplist,TW}}; + {error,{other_node,NodeId}} -> + 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, + {yes_no, Info, Fun}; + {error,not_found} -> + Info = "The port you are searching for could not be found.", + {info,Info} + end. + +detail_pages() -> + [{"General Information", fun init_gen_page/2}]. + +init_gen_page(Parent, Info) -> + Fields = info_fields(), + cdv_info_wx:start_link(Parent,{Fields,Info,[]}). + +format({I1,I2}) -> + "#Port<"++integer_to_list(I1) ++ "." ++ integer_to_list(I2) ++ ">"; +format(D) -> + D. + + +%%%----------------------------------------------------------------- +%%% Internal +info_fields() -> + [{"Overview", + [{"Name", name}, + {"Connected", {click,connected}}, + {"Slot", slot}, + {"Controls", controls}]}, + {scroll_boxes, + [{"Links",1,{click,links}}, + {"Monitors",1,{click,monitors}}]}]. diff --git a/lib/observer/src/cdv_port_wx.erl b/lib/observer/src/cdv_port_wx.erl deleted file mode 100644 index e34198ea0b..0000000000 --- a/lib/observer/src/cdv_port_wx.erl +++ /dev/null @@ -1,102 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_port_wx). - --export([col_to_elem/1, - col_spec/0, - get_info/1, - get_detail_cols/1, - get_details/1, - detail_pages/0, - format/1]). - --include_lib("wx/include/wx.hrl"). --include("crashdump_viewer.hrl"). - -%% Columns --define(COL_ID, 0). --define(COL_CONN, ?COL_ID+1). --define(COL_NAME, ?COL_CONN+1). --define(COL_CTRL, ?COL_NAME+1). --define(COL_SLOT, ?COL_CTRL+1). - - - -%% Callbacks for cdv_virtual_list -col_to_elem(id) -> col_to_elem(?COL_ID); -col_to_elem(?COL_ID) -> #port.id; -col_to_elem(?COL_CONN) -> #port.connected; -col_to_elem(?COL_NAME) -> #port.name; -col_to_elem(?COL_CTRL) -> #port.controls; -col_to_elem(?COL_SLOT) -> #port.slot. - -col_spec() -> - [{"Id", ?wxLIST_FORMAT_LEFT, 100}, - {"Connected", ?wxLIST_FORMAT_LEFT, 120}, - {"Name", ?wxLIST_FORMAT_LEFT, 150}, - {"Controls", ?wxLIST_FORMAT_LEFT, 200}, - {"Slot", ?wxLIST_FORMAT_RIGHT, 50}]. - -get_info(_) -> - {ok,Info,TW} = crashdump_viewer:ports(), - {Info,TW}. - -get_detail_cols(_) -> - {[?COL_ID,?COL_CONN],true}. - -%% Callbacks for cdv_detail_win -get_details(Id) -> - case crashdump_viewer:port(Id) of - {ok,Info,TW} -> - Proplist = - crashdump_viewer:to_proplist(record_info(fields,port),Info), - {ok,{Id,Proplist,TW}}; - {error,{other_node,NodeId}} -> - Info = "The port you are searching for was residing on " - "a remote node. No port information is available. " - "Show information about the remote node?", - {yes_no, Info, fun()->cdv_virtual_list:start_detail_win(NodeId) end}; - {error,not_found} -> - Info = "The port you are searching for could not be found.", - {info,Info} - end. - -detail_pages() -> - [{"General Information", fun init_gen_page/2}]. - -init_gen_page(Parent, Info) -> - Fields = info_fields(), - cdv_info_page:start_link(Parent,{Fields,Info,[]}). - -format({I1,I2}) -> - "#Port<"++integer_to_list(I1) ++ "." ++ integer_to_list(I2) ++ ">"; -format(D) -> - D. - - -%%%----------------------------------------------------------------- -%%% Internal -info_fields() -> - [{"Overview", - [{"Name", name}, - {"Connected", {click,connected}}, - {"Slot", slot}, - {"Controls", controls}]}, - {scroll_boxes, - [{"Links",1,{click,links}}, - {"Monitors",1,{click,monitors}}]}]. diff --git a/lib/observer/src/cdv_proc_cb.erl b/lib/observer/src/cdv_proc_cb.erl new file mode 100644 index 0000000000..dfc2df9c4c --- /dev/null +++ b/lib/observer/src/cdv_proc_cb.erl @@ -0,0 +1,156 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_proc_cb). + +-export([col_to_elem/1, + col_spec/0, + get_info/1, + get_detail_cols/1, + get_details/1, + detail_pages/0]). + +-include_lib("wx/include/wx.hrl"). +-include("crashdump_viewer.hrl"). + +%% Columns +-define(COL_ID, 0). +-define(COL_NAME, ?COL_ID+1). +-define(COL_STATE,?COL_NAME+1). +-define(COL_REDS, ?COL_STATE+1). +-define(COL_MEM, ?COL_REDS+1). +-define(COL_MSG, ?COL_MEM+1). + +%% Callbacks for cdv_virtual_list_wx +col_to_elem(id) -> col_to_elem(?COL_ID); +col_to_elem(?COL_ID) -> #proc.pid; +col_to_elem(?COL_NAME) -> #proc.name; +col_to_elem(?COL_STATE) -> #proc.state; +col_to_elem(?COL_MEM) -> #proc.memory; +col_to_elem(?COL_REDS) -> #proc.reds; +col_to_elem(?COL_MSG) -> #proc.msg_q_len. + +col_spec() -> + [{"Pid", ?wxLIST_FORMAT_CENTRE, 120}, + {"Name or Initial Func", ?wxLIST_FORMAT_LEFT, 250}, + {"State", ?wxLIST_FORMAT_LEFT, 100}, + {"Reds", ?wxLIST_FORMAT_RIGHT, 80}, + {"Memory", ?wxLIST_FORMAT_RIGHT, 80}, + {"MsgQ", ?wxLIST_FORMAT_RIGHT, 50}]. + +get_info(_) -> + {ok,Info,TW} = crashdump_viewer:processes(), + {Info,TW}. + +get_detail_cols(_) -> + {[?COL_ID],true}. + +%% Callbacks for cdv_detail_wx +get_details(Id) -> + case crashdump_viewer:proc_details(Id) of + {ok,Info,TW} -> + %% The following table is used by observer_html_lib + %% for storing expanded terms and it is read by + %% cdv_html_wx when a link to an expandable term is clicked. + Tab = ets:new(cdv_expand,[set,public]), + Proplist0 = + crashdump_viewer:to_proplist(record_info(fields,proc),Info), + Proplist = [{expand_table,Tab}|Proplist0], + Title = io_lib:format("~s (~s)",[Info#proc.name, Id]), + {ok,{Title,Proplist,TW}}; + {error,{other_node,NodeId}} -> + 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, + {yes_no, Info, Fun}; + {error,not_found} -> + Info = "The process you are searching for could not be found.", + {info,Info} + end. + +detail_pages() -> + [{"General Information", fun init_gen_page/2}, + {"Messages", fun init_message_page/2}, + {"Dictionary", fun init_dict_page/2}, + {"Stack Dump", fun init_stack_page/2}, + {"ETS tables", fun init_ets_page/2}, + {"Timers", fun init_timer_page/2}]. + +init_gen_page(Parent, Info) -> + Fields = info_fields(), + cdv_info_wx:start_link(Parent,{Fields,Info,[]}). + +init_message_page(Parent, Info) -> + init_memory_page(Parent, Info, msg_q, "MsgQueue"). + +init_dict_page(Parent, Info) -> + init_memory_page(Parent, Info, dict, "Dictionary"). + +init_stack_page(Parent, Info) -> + init_memory_page(Parent, Info, stack_dump, "StackDump"). + +init_memory_page(Parent, Info0, Tag, Heading) -> + Info = proplists:get_value(Tag,Info0), + Tab = proplists:get_value(expand_table,Info0), + Html = observer_html_lib:expandable_term(Heading,Info,Tab), + cdv_html_wx:start_link(Parent,{expand,Html,Tab}). + +init_ets_page(Parent, Info) -> + Pid = proplists:get_value(pid,Info), + cdv_virtual_list_wx:start_link(Parent, cdv_ets_cb, Pid). + +init_timer_page(Parent, Info) -> + Pid = proplists:get_value(pid,Info), + cdv_virtual_list_wx:start_link(Parent, cdv_timer_cb, Pid). + +%%%----------------------------------------------------------------- +%%% Internal +info_fields() -> + [{"Overview", + [{"Initial Call", init_func}, + {dynamic, current_func}, + {"Registered Name", name}, + {"Status", state}, + {"Started", start_time}, + {"Parent", {click,parent}}, + {"Message Queue Len",msg_q_len}, + {"Reductions", reds}, + {"Program counter", prog_count}, + {"Continuation pointer",cp}, + {"Arity",arity}]}, + {scroll_boxes, + [{"Last Calls",1,{plain,last_calls}}]}, + {scroll_boxes, + [{"Links",1,{click,links}}, + {"Monitors",2,{click,monitors}}, + {"Monitored By",2,{click,mon_by}}]}, + {"Memory and Garbage Collection", + [{"Memory", memory}, + {"Stack and Heap", stack_heap}, + {"Old Heap", old_heap}, + {"Heap Unused", heap_unused}, + {"Old Heap Unused", old_heap_unused}, + {"Number of Heap Fragements", num_heap_frag}, + {"Heap Fragment Data",heap_frag_data}, + {"New Heap Start", new_heap_start}, + {"New Heap Top", new_heap_top}, + {"Stack Top", stack_top}, + {"Stack End", stack_end}, + {"Old Heap Start", old_heap_start}, + {"Old Heap Top", old_heap_top}, + {"Old Heap End", old_heap_end}]}]. diff --git a/lib/observer/src/cdv_proc_wx.erl b/lib/observer/src/cdv_proc_wx.erl deleted file mode 100644 index 6a8fa0b9be..0000000000 --- a/lib/observer/src/cdv_proc_wx.erl +++ /dev/null @@ -1,155 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_proc_wx). - --export([col_to_elem/1, - col_spec/0, - get_info/1, - get_detail_cols/1, - get_details/1, - detail_pages/0]). - --include_lib("wx/include/wx.hrl"). --include("crashdump_viewer.hrl"). - -%% Columns --define(COL_ID, 0). --define(COL_NAME, ?COL_ID+1). --define(COL_STATE,?COL_NAME+1). --define(COL_REDS, ?COL_STATE+1). --define(COL_MEM, ?COL_REDS+1). --define(COL_MSG, ?COL_MEM+1). - -%% Callbacks for cdv_virtual_list -col_to_elem(id) -> col_to_elem(?COL_ID); -col_to_elem(?COL_ID) -> #proc.pid; -col_to_elem(?COL_NAME) -> #proc.name; -col_to_elem(?COL_STATE) -> #proc.state; -col_to_elem(?COL_MEM) -> #proc.memory; -col_to_elem(?COL_REDS) -> #proc.reds; -col_to_elem(?COL_MSG) -> #proc.msg_q_len. - -col_spec() -> - [{"Pid", ?wxLIST_FORMAT_CENTRE, 120}, - {"Name or Initial Func", ?wxLIST_FORMAT_LEFT, 250}, - {"State", ?wxLIST_FORMAT_LEFT, 100}, - {"Reds", ?wxLIST_FORMAT_RIGHT, 80}, - {"Memory", ?wxLIST_FORMAT_RIGHT, 80}, - {"MsgQ", ?wxLIST_FORMAT_RIGHT, 50}]. - -get_info(_) -> - {ok,Info,TW} = crashdump_viewer:processes(), - {Info,TW}. - -get_detail_cols(_) -> - {[?COL_ID],true}. - -%% Callbacks for cdv_detail_win -get_details(Id) -> - case crashdump_viewer:proc_details(Id) of - {ok,Info,TW} -> - %% The following table is used by crashdump_viewer_html - %% for storing expanded terms and it is read by - %% cdv_html_page when a link to an expandable term is clicked. - Tab = ets:new(cdv_expand,[set,public]), - Proplist0 = - crashdump_viewer:to_proplist(record_info(fields,proc),Info), - Proplist = [{expand_table,Tab}|Proplist0], - Title = io_lib:format("~s (~s)",[Info#proc.name, Id]), - {ok,{Title,Proplist,TW}}; - {error,{other_node,NodeId}} -> - Info = "The process you are searching for was residing on " - "a remote node. No process information is available. " - "Show information about the remote node?", - {yes_no, Info, fun()->cdv_virtual_list:start_detail_win(NodeId) end}; - {error,not_found} -> - Info = "The process you are searching for could not be found.", - {info,Info} - end. - -detail_pages() -> - [{"General Information", fun init_gen_page/2}, - {"Messages", fun init_message_page/2}, - {"Dictionary", fun init_dict_page/2}, - {"Stack Dump", fun init_stack_page/2}, - {"ETS tables", fun init_ets_page/2}, - {"Timers", fun init_timer_page/2}]. - -init_gen_page(Parent, Info) -> - Fields = info_fields(), - cdv_info_page:start_link(Parent,{Fields,Info,[]}). - -init_message_page(Parent, Info) -> - init_memory_page(Parent, Info, msg_q, "MsgQueue"). - -init_dict_page(Parent, Info) -> - init_memory_page(Parent, Info, dict, "Dictionary"). - -init_stack_page(Parent, Info) -> - init_memory_page(Parent, Info, stack_dump, "StackDump"). - -init_memory_page(Parent, Info0, Tag, Heading) -> - Info = proplists:get_value(Tag,Info0), - Tab = proplists:get_value(expand_table,Info0), - Html = crashdump_viewer_html:expandable_term(Heading,Info,Tab), - cdv_html_page:start_link(Parent,{expand,Html,Tab}). - -init_ets_page(Parent, Info) -> - Pid = proplists:get_value(pid,Info), - cdv_virtual_list:start_link(Parent, cdv_ets_wx, Pid). - -init_timer_page(Parent, Info) -> - Pid = proplists:get_value(pid,Info), - cdv_virtual_list:start_link(Parent, cdv_timer_wx, Pid). - -%%%----------------------------------------------------------------- -%%% Internal -info_fields() -> - [{"Overview", - [{"Initial Call", init_func}, - {dynamic, current_func}, - {"Registered Name", name}, - {"Status", state}, - {"Started", start_time}, - {"Parent", {click,parent}}, - {"Message Queue Len",msg_q_len}, - {"Reductions", reds}, - {"Program counter", prog_count}, - {"Continuation pointer",cp}, - {"Arity",arity}]}, - {scroll_boxes, - [{"Last Calls",1,{plain,last_calls}}]}, - {scroll_boxes, - [{"Links",1,{click,links}}, - {"Monitors",2,{click,monitors}}, - {"Monitored By",2,{click,mon_by}}]}, - {"Memory and Garbage Collection", - [{"Memory", memory}, - {"Stack and Heap", stack_heap}, - {"Old Heap", old_heap}, - {"Heap Unused", heap_unused}, - {"Old Heap Unused", old_heap_unused}, - {"Number of Heap Fragements", num_heap_frag}, - {"Heap Fragment Data",heap_frag_data}, - {"New Heap Start", new_heap_start}, - {"New Heap Top", new_heap_top}, - {"Stack Top", stack_top}, - {"Stack End", stack_end}, - {"Old Heap Start", old_heap_start}, - {"Old Heap Top", old_heap_top}, - {"Old Heap End", old_heap_end}]}]. diff --git a/lib/observer/src/cdv_table_page.erl b/lib/observer/src/cdv_table_page.erl deleted file mode 100644 index 71ec7686ce..0000000000 --- a/lib/observer/src/cdv_table_page.erl +++ /dev/null @@ -1,106 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% 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 -%% 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_table_page). - --behaviour(wx_object). - --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_cast/2]). - --include_lib("wx/include/wx.hrl"). --include("observer_defs.hrl"). - -%% Records --record(state, - {trunc_warn=[]}). - -start_link(ParentWin, Info) -> - wx_object:start_link(?MODULE, [ParentWin, Info], []). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -init([ParentWin, Callback]) when is_atom(Callback) -> - {ok,TableInfo} = Callback:get_info(), - init([ParentWin, TableInfo]); - -init([ParentWin, {ColumnSpec,Info,TW}]) -> - Style0 = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES bor ?wxLC_VRULES, - Style = - case lists:all(fun({"",_,_}) -> true; (_) -> false end, ColumnSpec) of - true -> Style0 bor ?wxLC_NO_HEADER; - false -> Style0 - end, - Grid = wxListCtrl:new(ParentWin, [{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, - lists:foldl(AddListEntry, 0, ColumnSpec), - wxListItem:destroy(Li), - Insert = fun(RowData, Row) -> - wxListCtrl:insertItem(Grid, Row, ""), - set_items(Grid,Row,RowData,0), - Row + 1 - end, - lists:foldl(Insert, 0, Info), - {Grid, #state{trunc_warn=TW}}. - -%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -handle_info(active, State) -> - crashdump_viewer_wx:set_status(State#state.trunc_warn), - {noreply, State}; - -handle_info(Info, State) -> - io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]), - {noreply, State}. - -terminate(_Reason, _State) -> - ok. - -code_change(_, _, State) -> - {ok, State}. - -handle_call(Msg, _From, State) -> - io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]), - {reply, ok, State}. - -handle_cast(Msg, State) -> - io:format("~p~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]), - {noreply, State}. - -handle_event(Event, State) -> - io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]), - {noreply, State}. - -%%%%%%%%%%%%%%%%%%%%%%% Internal %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -set_items(Grid,Row,[Col|Cols],ColN) -> - Str = case Col of - undefined -> ""; - _ -> observer_lib:to_str(Col) - end, - wxListCtrl:setItem(Grid, Row, ColN, Str), - set_items(Grid,Row,Cols,ColN+1); -set_items(_,_,[],_) -> - ok. diff --git a/lib/observer/src/cdv_table_wx.erl b/lib/observer/src/cdv_table_wx.erl new file mode 100644 index 0000000000..f8943db17d --- /dev/null +++ b/lib/observer/src/cdv_table_wx.erl @@ -0,0 +1,106 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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_table_wx). + +-behaviour(wx_object). + +-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_cast/2]). + +-include_lib("wx/include/wx.hrl"). +-include("observer_defs.hrl"). + +%% Records +-record(state, + {trunc_warn=[]}). + +start_link(ParentWin, Info) -> + wx_object:start_link(?MODULE, [ParentWin, Info], []). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([ParentWin, Callback]) when is_atom(Callback) -> + {ok,TableInfo} = Callback:get_info(), + init([ParentWin, TableInfo]); + +init([ParentWin, {ColumnSpec,Info,TW}]) -> + Style0 = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES bor ?wxLC_VRULES, + Style = + case lists:all(fun({"",_,_}) -> true; (_) -> false end, ColumnSpec) of + true -> Style0 bor ?wxLC_NO_HEADER; + false -> Style0 + end, + Grid = wxListCtrl:new(ParentWin, [{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, + lists:foldl(AddListEntry, 0, ColumnSpec), + wxListItem:destroy(Li), + Insert = fun(RowData, Row) -> + wxListCtrl:insertItem(Grid, Row, ""), + set_items(Grid,Row,RowData,0), + Row + 1 + end, + lists:foldl(Insert, 0, Info), + {Grid, #state{trunc_warn=TW}}. + +%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +handle_info(active, State) -> + cdv_wx:set_status(State#state.trunc_warn), + {noreply, State}; + +handle_info(Info, State) -> + io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_, _, State) -> + {ok, State}. + +handle_call(Msg, _From, State) -> + io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]), + {reply, ok, State}. + +handle_cast(Msg, State) -> + io:format("~p~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]), + {noreply, State}. + +handle_event(Event, State) -> + io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]), + {noreply, State}. + +%%%%%%%%%%%%%%%%%%%%%%% Internal %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +set_items(Grid,Row,[Col|Cols],ColN) -> + Str = case Col of + undefined -> ""; + _ -> observer_lib:to_str(Col) + end, + wxListCtrl:setItem(Grid, Row, ColN, Str), + set_items(Grid,Row,Cols,ColN+1); +set_items(_,_,[],_) -> + ok. diff --git a/lib/observer/src/cdv_term_cb.erl b/lib/observer/src/cdv_term_cb.erl new file mode 100644 index 0000000000..6426cc0803 --- /dev/null +++ b/lib/observer/src/cdv_term_cb.erl @@ -0,0 +1,75 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_term_cb). + +-export([get_details/1, + detail_pages/0]). + +%% Callbacks for cdv_detail_wx +get_details({_, {T,Key}}) -> + [{Key,Term}] = ets:lookup(T,Key), + {ok,{"Expanded Term", [Term, T], []}}. + +detail_pages() -> + [{"Term", fun init_term_page/2}]. + +init_term_page(ParentWin, [Term, Tab]) -> + Expanded = expand(Term, true), + BinSaved = expand(Term, Tab), + cdv_multi_wx:start_link( + ParentWin, + [{"Format \~p",cdv_html_wx,format_term_fun("~p",BinSaved,Tab)}, + {"Format \~tp",cdv_html_wx,format_term_fun("~tp",BinSaved,Tab)}, + {"Format \~w",cdv_html_wx,format_term_fun("~w",BinSaved,Tab)}, + {"Format \~s",cdv_html_wx,format_term_fun("~s",Expanded,Tab)}, + {"Format \~ts",cdv_html_wx,format_term_fun("~ts",Expanded,Tab)}]). + +format_term_fun(Format,Term,Tab) -> + fun() -> + try io_lib:format(Format,[Term]) of + Str -> {expand, plain_html(Str), Tab} + catch error:badarg -> + Warning = "This term can not be formatted with " ++ Format, + observer_html_lib:warning(Warning) + end + end. + +plain_html(Text) -> + observer_html_lib:plain_page(Text). + +expand(['#CDVBin',Offset,Size,Pos], true) -> + {ok,Bin} = crashdump_viewer:expand_binary({Offset,Size,Pos}), + Bin; +expand(Bin, Tab) when is_binary(Bin), not is_boolean(Tab) -> + <> = Bin, + Size = byte_size(Bin), + Hash = erlang:phash2(Bin), + Key = {Preview, Size, Hash}, + ets:insert(Tab, {Key,Bin}), + ['#OBSBin',Preview,Size,Hash]; +expand([H|T], Expand) -> + case expand(T, Expand) of + ET when is_list(ET) -> + [expand(H, Expand)|ET]; + ET -> % The tail is an expanded binary - cannot append with | + [expand(H, Expand),ET] + end; +expand(Tuple, Expand) when is_tuple(Tuple) -> + list_to_tuple(expand(tuple_to_list(Tuple), Expand)); +expand(Term, _) -> + Term. diff --git a/lib/observer/src/cdv_term_wx.erl b/lib/observer/src/cdv_term_wx.erl deleted file mode 100644 index 9b83249eae..0000000000 --- a/lib/observer/src/cdv_term_wx.erl +++ /dev/null @@ -1,75 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_term_wx). - --export([get_details/1, - detail_pages/0]). - -%% Callbacks for cdv_detail_win -get_details({_, {T,Key}}) -> - [{Key,Term}] = ets:lookup(T,Key), - {ok,{"Expanded Term", [Term, T], []}}. - -detail_pages() -> - [{"Term", fun init_term_page/2}]. - -init_term_page(ParentWin, [Term, Tab]) -> - Expanded = expand(Term, true), - BinSaved = expand(Term, Tab), - cdv_multi_panel:start_link( - ParentWin, - [{"Format \~p",cdv_html_page,format_term_fun("~p",BinSaved,Tab)}, - {"Format \~tp",cdv_html_page,format_term_fun("~tp",BinSaved,Tab)}, - {"Format \~w",cdv_html_page,format_term_fun("~w",BinSaved,Tab)}, - {"Format \~s",cdv_html_page,format_term_fun("~s",Expanded,Tab)}, - {"Format \~ts",cdv_html_page,format_term_fun("~ts",Expanded,Tab)}]). - -format_term_fun(Format,Term,Tab) -> - fun() -> - try io_lib:format(Format,[Term]) of - Str -> {expand, plain_html(Str), Tab} - catch error:badarg -> - Warning = "This term can not be formatted with " ++ Format, - crashdump_viewer_html:warning(Warning) - end - end. - -plain_html(Text) -> - crashdump_viewer_html:plain_page(Text). - -expand(['#CDVBin',Offset,Size,Pos], true) -> - {ok,Bin} = crashdump_viewer:expand_binary({Offset,Size,Pos}), - Bin; -expand(Bin, Tab) when is_binary(Bin), not is_boolean(Tab) -> - <> = Bin, - Size = byte_size(Bin), - Hash = erlang:phash2(Bin), - Key = {Preview, Size, Hash}, - ets:insert(Tab, {Key,Bin}), - ['#OBSBin',Preview,Size,Hash]; -expand([H|T], Expand) -> - case expand(T, Expand) of - ET when is_list(ET) -> - [expand(H, Expand)|ET]; - ET -> % The tail is an expanded binary - cannot append with | - [expand(H, Expand),ET] - end; -expand(Tuple, Expand) when is_tuple(Tuple) -> - list_to_tuple(expand(tuple_to_list(Tuple), Expand)); -expand(Term, _) -> - Term. diff --git a/lib/observer/src/cdv_timer_cb.erl b/lib/observer/src/cdv_timer_cb.erl new file mode 100644 index 0000000000..9cdbfa05a9 --- /dev/null +++ b/lib/observer/src/cdv_timer_cb.erl @@ -0,0 +1,51 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_timer_cb). + +-export([col_to_elem/1, + col_spec/0, + get_info/1, + get_detail_cols/1]). + +-include_lib("wx/include/wx.hrl"). +-include("crashdump_viewer.hrl"). + +%% Defines +-define(COL_OWNER, 0). +-define(COL_MSG, ?COL_OWNER+1). +-define(COL_TIME, ?COL_MSG+1). + +%% Callbacks for cdv_virtual_list_wx +col_to_elem(id) -> col_to_elem(?COL_OWNER); +col_to_elem(?COL_OWNER) -> #timer.pid; +col_to_elem(?COL_MSG) -> #timer.msg; +col_to_elem(?COL_TIME) -> #timer.time. + +col_spec() -> + [{"Owner", ?wxLIST_FORMAT_LEFT, 110}, + {"Message", ?wxLIST_FORMAT_LEFT, 400}, + {"Time left (ms)", ?wxLIST_FORMAT_RIGHT, 80}]. + +get_info(Owner) -> + {ok,Info,TW} = crashdump_viewer:timers(Owner), + {Info,TW}. + +get_detail_cols(all) -> + {[?COL_OWNER],false}; +get_detail_cols(_) -> + {[],false}. diff --git a/lib/observer/src/cdv_timer_wx.erl b/lib/observer/src/cdv_timer_wx.erl deleted file mode 100644 index 2bd250a46c..0000000000 --- a/lib/observer/src/cdv_timer_wx.erl +++ /dev/null @@ -1,51 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_timer_wx). - --export([col_to_elem/1, - col_spec/0, - get_info/1, - get_detail_cols/1]). - --include_lib("wx/include/wx.hrl"). --include("crashdump_viewer.hrl"). - -%% Defines --define(COL_OWNER, 0). --define(COL_MSG, ?COL_OWNER+1). --define(COL_TIME, ?COL_MSG+1). - -%% Callbacks for cdv_virtual_list -col_to_elem(id) -> col_to_elem(?COL_OWNER); -col_to_elem(?COL_OWNER) -> #timer.pid; -col_to_elem(?COL_MSG) -> #timer.msg; -col_to_elem(?COL_TIME) -> #timer.time. - -col_spec() -> - [{"Owner", ?wxLIST_FORMAT_LEFT, 110}, - {"Message", ?wxLIST_FORMAT_LEFT, 400}, - {"Time left (ms)", ?wxLIST_FORMAT_RIGHT, 80}]. - -get_info(Owner) -> - {ok,Info,TW} = crashdump_viewer:timers(Owner), - {Info,TW}. - -get_detail_cols(all) -> - {[?COL_OWNER],false}; -get_detail_cols(_) -> - {[],false}. diff --git a/lib/observer/src/cdv_virtual_list.erl b/lib/observer/src/cdv_virtual_list.erl deleted file mode 100644 index 5897b9d02a..0000000000 --- a/lib/observer/src/cdv_virtual_list.erl +++ /dev/null @@ -1,414 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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_virtual_list). - --behaviour(wx_object). - --export([start_link/2, start_link/3, start_detail_win/1]). - -%% wx_object callbacks --export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, - handle_event/2, handle_cast/2]). - --include_lib("wx/include/wx.hrl"). --include("observer_defs.hrl"). - -%% Defines --define(COL_ID, 0). --define(ID_DETAILS, 202). - -%% Records - --record(sort, - { - sort_key, - sort_incr=true - }). - --record(holder, {parent, - info, - last_row, - sort, - attrs, - callback - }). - --record(state, {grid, - panel, - detail_wins=[], - holder, - callback, - trunc_warn=[], - menu_cols=[], % columns to show in right click menu - menu_items=[]}). % right click menu items for the selected row - -start_link(ParentWin, Callback) -> - wx_object:start_link({local,Callback},?MODULE, - [ParentWin, Callback, all], []). - -start_link(ParentWin, Callback, Owner) -> - wx_object:start_link(?MODULE, [ParentWin, Callback, Owner], []). - -start_detail_win(Id) -> - Callback = - case Id of - "<"++_ -> - cdv_proc_wx; - "#Port"++_ -> - cdv_port_wx; - _ -> - case catch list_to_integer(Id) of - NodeId when is_integer(NodeId) -> - cdv_dist_wx; - _ -> - cdv_mod_wx - end - end, - start_detail_win(Callback,Id). -start_detail_win(Callback,Id) -> - wx_object:cast(Callback,{start_detail_win,Id}). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -init([ParentWin, Callback, Owner]) -> - {Holder,TW} = spawn_table_holder(Callback, Owner), - Panel = wxPanel:new(ParentWin), - {Grid,MenuCols} = create_list_box(Panel, Holder, Callback, Owner), - Sizer = wxBoxSizer:new(?wxVERTICAL), - wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL}, - {proportion, 1}, - {border,4}]), - - wxWindow:setSizer(Panel, Sizer), - - State = #state{grid=Grid, - panel=Panel, - holder=Holder, - callback=Callback, - trunc_warn=TW, - menu_cols=MenuCols - }, - {Panel, State}. - -%% UI-creation - -create_list_box(Panel, Holder, Callback, Owner) -> - Style = - ?wxLC_SINGLE_SEL bor ?wxLC_REPORT bor ?wxLC_VIRTUAL bor - ?wxLC_HRULES bor ?wxHSCROLL bor ?wxVSCROLL, - ListCtrl = wxListCtrl:new(Panel, [{style, Style}, - {onGetItemText, - fun(_, Row, Col) -> - call(Holder, {get_row, self(), Row, Col}) - end}, - {onGetItemAttr, - fun(_, Item) -> - call(Holder, {get_attr, self(), Item}) - end} - ]), - Li = wxListItem:new(), - AddListEntry = fun({Name, Align, DefSize}, Col) -> - wxListItem:setText(Li, Name), - wxListItem:setAlign(Li, Align), - wxListCtrl:insertColumn(ListCtrl, Col, Li), - wxListCtrl:setColumnWidth(ListCtrl, Col, DefSize), - Col + 1 - end, - ListItems = Callback:col_spec(), - lists:foldl(AddListEntry, 0, ListItems), - wxListItem:destroy(Li), - - wxListCtrl:setItemCount(ListCtrl, 0), - wxListCtrl:connect(ListCtrl, size, [{skip, true}]), - wxListCtrl:connect(ListCtrl, command_list_col_click), - - - %% If detail pages can be opened from this list - catch double - %% click and right click - DetailCols = - case catch Callback:get_detail_cols(Owner) of - {DC,DoubleClick} when is_list(DC), DC=/=[] -> - wxListCtrl:connect(ListCtrl, command_list_item_right_click), - if DoubleClick -> - wxListCtrl:connect(ListCtrl, command_list_item_activated); - true -> - ok - end, - DC; - _ -> - [] - end, - - {ListCtrl,DetailCols}. - -do_start_detail_win(undefined, State) -> - State; -do_start_detail_win(Id, #state{panel=Panel,detail_wins=Opened, - callback=Callback}=State) -> - NewOpened = - case lists:keyfind(Id, 1, Opened) of - false -> - case cdv_detail_win:start_link(Id, Panel, Callback) of - {error, _} -> - Opened; - IW -> - [{Id, IW} | Opened] - end; - {_, IW} -> - wxFrame:raise(IW), - Opened - end, - State#state{detail_wins=NewOpened}. - -call(Holder, What) when is_atom(Holder) -> - call(whereis(Holder), What); -call(Holder, What) when is_pid(Holder) -> - Ref = erlang:monitor(process, Holder), - Holder ! What, - receive - {'DOWN', Ref, _, _, _} -> ""; - {Holder, Res} -> - erlang:demonitor(Ref), - Res - after 5000 -> - io:format("Hanging call ~p~n",[What]), - "" - end; -call(_,_) -> - "". - - -%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -handle_info({holder_updated, Count}, State=#state{grid=Grid}) -> - wxListCtrl:setItemCount(Grid, Count), - Count > 0 andalso wxListCtrl:refreshItems(Grid, 0, Count-1), - {noreply, State}; - -handle_info(active, State) -> - crashdump_viewer_wx:set_status(State#state.trunc_warn), - {noreply, State}; - -handle_info(Info, State) -> - io:format("~p:~p, Unexpected info: ~p~n", [?MODULE, ?LINE, Info]), - {noreply, State}. - -terminate(_Reason, #state{holder=Holder}) -> - Holder ! stop, - ok. - -code_change(_, _, State) -> - {ok, State}. - -handle_call(new_dump, _From, - #state{grid=Grid,detail_wins=Opened, - holder=Holder,callback=Callback}=State) -> - lists:foreach(fun({_Id, IW}) -> wxFrame:destroy(IW) end, Opened), - wxListCtrl:deleteAllItems(Grid), - Ref = erlang:monitor(process,Holder), - Holder ! stop, - receive {'DOWN',Ref,_,_,_} -> ok end, - {NewHolder,TW} = spawn_table_holder(Callback, all), - {reply, ok, State#state{detail_wins=[],holder=NewHolder,trunc_warn=TW}}; - -handle_call(Msg, _From, State) -> - io:format("~p:~p: Unhandled call ~p~n",[?MODULE, ?LINE, Msg]), - {reply, ok, State}. - -handle_cast({start_detail_win,Id}, State) -> - State2 = do_start_detail_win(Id, State), - {noreply, State2}; - -handle_cast({detail_win_closed, Id},#state{detail_wins=Opened}=State) -> - Opened2 = lists:keydelete(Id, 1, Opened), - {noreply, State#state{detail_wins=Opened2}}; - -handle_cast(Msg, State) -> - io:format("~p:~p: Unhandled cast ~p~n", [?MODULE, ?LINE, Msg]), - {noreply, State}. - -%%%%%%%%%%%%%%%%%%%%LOOP%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -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); - false -> - ok - end, - {noreply, State}; - -handle_event(#wx{event=#wxSize{size={W,_}}}, - #state{grid=Grid}=State) -> - observer_lib:set_listctrl_col_size(Grid, W), - {noreply, State}; - -handle_event(#wx{event=#wxList{type=command_list_item_right_click, - itemIndex=Row}}, - #state{panel=Panel, holder=Holder, menu_cols=MenuCols} = State) -> - Menu = wxMenu:new(), - MenuItems = - lists:flatmap( - fun(Col) -> - MenuId = ?ID_DETAILS + Col, - ColText = call(Holder, {get_row, self(), Row, Col}), - case ColText of - "[]" -> []; - _ -> - What = - case catch list_to_integer(ColText) of - NodeId when is_integer(NodeId) -> - "node " ++ ColText; - _ -> - ColText - end, - Text = "Properties for " ++ What, - wxMenu:append(Menu, MenuId, Text), - [{MenuId,ColText}] - end - end, - MenuCols), - wxWindow:popupMenu(Panel, Menu), - wxMenu:destroy(Menu), - {noreply,State#state{menu_items=MenuItems}}; - -handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}}, - #state{holder=Holder}=State) -> - Holder ! {change_sort, Col}, - {noreply, State}; - -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), - {noreply, State}; - -handle_event(Event, State) -> - io:format("~p:~p: handle event ~p\n", [?MODULE, ?LINE, Event]), - {noreply, State}. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%TABLE HOLDER%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -spawn_table_holder(Callback, Owner) -> - {Info,TW} = Callback:get_info(Owner), - Attrs = observer_lib:create_attrs(), - Parent = self(), - Holder = - case Owner of - all -> - Name = list_to_atom(atom_to_list(Callback) ++ "__holder"), - spawn_link( - fun() -> - register(Name,self()), - init_table_holder(Parent, Attrs, Callback, Info) - end), - Name; - _ -> - spawn_link( - fun() -> - init_table_holder(Parent, Attrs, Callback, Info) - end) - end, - {Holder,TW}. - -init_table_holder(Parent, Attrs, Callback, InfoList0) -> - Sort = #sort{sort_key=Callback:col_to_elem(id)}, - {_Sort, InfoList} = do_sort(Sort,InfoList0), - Info = array:from_list(InfoList), - NRows = array:size(Info), - Parent ! {holder_updated, NRows}, - table_holder(#holder{parent=Parent, - info=Info, - sort=Sort, - attrs=Attrs, - callback=Callback}). - -table_holder(#holder{callback=Callback, attrs=Attrs}=S0) -> - receive - _M={get_row, From, Row, Col} -> - %% erlang:display(_M), - State = get_row(From, Row, Col, S0), - table_holder(State); - _M={get_attr, From, Row} -> - %% erlang:display(_M), - get_attr(From, Row, Attrs), - table_holder(S0); - _M={change_sort, Col} -> - %% erlang:display(_M), - State = change_sort(Callback:col_to_elem(Col), S0), - table_holder(State); - stop -> - ok; - What -> - io:format("Table holder got ~p~n",[What]), - table_holder(S0) - end. - -change_sort(Col, S0=#holder{parent=Parent, info=Info0, sort=Sort0}) -> - NRows = array:size(Info0), - InfoList0 = array:to_list(Info0), - {Sort, InfoList}=sort(Col, Sort0, InfoList0), - Info = array:from_list(InfoList), - Parent ! {holder_updated, NRows}, - S0#holder{info=Info, last_row=undefined, sort=Sort}. - -sort(Col, Opt=#sort{sort_key=Col, sort_incr=Bool}, Table) -> - do_sort(Opt#sort{sort_incr=not Bool}, Table); -sort(Col, Sort,Table) -> - do_sort(Sort#sort{sort_key=Col, sort_incr=true}, Table). - -do_sort(Sort=#sort{sort_key=Col, sort_incr=true}, Table) -> - {Sort, lists:keysort(Col, Table)}; -do_sort(Sort=#sort{sort_key=Col, sort_incr=false}, Table) -> - {Sort, lists:reverse(lists:keysort(Col, Table))}. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -get_cell_data(Callback, ColNo, RowInfo) -> - case element(Callback:col_to_elem(ColNo), RowInfo) of - undefined -> ""; - Cell -> try Callback:format(Cell) catch error:undef -> Cell end - end. - -get_row(From, Row, Col, - #holder{callback=Callback, last_row={Row,RowInfo}}=State) -> - Data = get_cell_data(Callback, Col, RowInfo), - From ! {self(), observer_lib:to_str(Data)}, - State; -get_row(From, Row, Col, #holder{callback=Callback, info=Info}=S0) -> - {Data,State} = - case Row >= array:size(Info) of - true -> - {"",S0}; - false -> - RowInfo = array:get(Row, Info), - CellData = get_cell_data(Callback, Col, RowInfo), - {CellData,S0#holder{last_row={Row,RowInfo}}} - end, - From ! {self(), observer_lib:to_str(Data)}, - State. - -get_attr(From, Row, Attrs) -> - Attribute = case Row rem 2 =:= 0 of - true -> Attrs#attrs.even; - false -> Attrs#attrs.odd - end, - From ! {self(), Attribute}. diff --git a/lib/observer/src/cdv_virtual_list_wx.erl b/lib/observer/src/cdv_virtual_list_wx.erl new file mode 100644 index 0000000000..c5a7d9a2e5 --- /dev/null +++ b/lib/observer/src/cdv_virtual_list_wx.erl @@ -0,0 +1,414 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_virtual_list_wx). + +-behaviour(wx_object). + +-export([start_link/2, start_link/3, start_detail_win/1]). + +%% wx_object callbacks +-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, + handle_event/2, handle_cast/2]). + +-include_lib("wx/include/wx.hrl"). +-include("observer_defs.hrl"). + +%% Defines +-define(COL_ID, 0). +-define(ID_DETAILS, 202). + +%% Records + +-record(sort, + { + sort_key, + sort_incr=true + }). + +-record(holder, {parent, + info, + last_row, + sort, + attrs, + callback + }). + +-record(state, {grid, + panel, + detail_wins=[], + holder, + callback, + trunc_warn=[], + menu_cols=[], % columns to show in right click menu + menu_items=[]}). % right click menu items for the selected row + +start_link(ParentWin, Callback) -> + wx_object:start_link({local,Callback},?MODULE, + [ParentWin, Callback, all], []). + +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) -> + wx_object:cast(Callback,{start_detail_win,Id}). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +init([ParentWin, Callback, Owner]) -> + {Holder,TW} = spawn_table_holder(Callback, Owner), + Panel = wxPanel:new(ParentWin), + {Grid,MenuCols} = create_list_box(Panel, Holder, Callback, Owner), + Sizer = wxBoxSizer:new(?wxVERTICAL), + wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL}, + {proportion, 1}, + {border,4}]), + + wxWindow:setSizer(Panel, Sizer), + + State = #state{grid=Grid, + panel=Panel, + holder=Holder, + callback=Callback, + trunc_warn=TW, + menu_cols=MenuCols + }, + {Panel, State}. + +%% UI-creation + +create_list_box(Panel, Holder, Callback, Owner) -> + Style = + ?wxLC_SINGLE_SEL bor ?wxLC_REPORT bor ?wxLC_VIRTUAL bor + ?wxLC_HRULES bor ?wxHSCROLL bor ?wxVSCROLL, + ListCtrl = wxListCtrl:new(Panel, [{style, Style}, + {onGetItemText, + fun(_, Row, Col) -> + call(Holder, {get_row, self(), Row, Col}) + end}, + {onGetItemAttr, + fun(_, Item) -> + call(Holder, {get_attr, self(), Item}) + end} + ]), + Li = wxListItem:new(), + AddListEntry = fun({Name, Align, DefSize}, Col) -> + wxListItem:setText(Li, Name), + wxListItem:setAlign(Li, Align), + wxListCtrl:insertColumn(ListCtrl, Col, Li), + wxListCtrl:setColumnWidth(ListCtrl, Col, DefSize), + Col + 1 + end, + ListItems = Callback:col_spec(), + lists:foldl(AddListEntry, 0, ListItems), + wxListItem:destroy(Li), + + wxListCtrl:setItemCount(ListCtrl, 0), + wxListCtrl:connect(ListCtrl, size, [{skip, true}]), + wxListCtrl:connect(ListCtrl, command_list_col_click), + + + %% If detail pages can be opened from this list - catch double + %% click and right click + DetailCols = + case catch Callback:get_detail_cols(Owner) of + {DC,DoubleClick} when is_list(DC), DC=/=[] -> + wxListCtrl:connect(ListCtrl, command_list_item_right_click), + if DoubleClick -> + wxListCtrl:connect(ListCtrl, command_list_item_activated); + true -> + ok + end, + DC; + _ -> + [] + end, + + {ListCtrl,DetailCols}. + +do_start_detail_win(undefined, State) -> + State; +do_start_detail_win(Id, #state{panel=Panel,detail_wins=Opened, + 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] + end; + {_, IW} -> + wxFrame:raise(IW), + Opened + end, + State#state{detail_wins=NewOpened}. + +call(Holder, What) when is_atom(Holder) -> + call(whereis(Holder), What); +call(Holder, What) when is_pid(Holder) -> + Ref = erlang:monitor(process, Holder), + Holder ! What, + receive + {'DOWN', Ref, _, _, _} -> ""; + {Holder, Res} -> + erlang:demonitor(Ref), + Res + after 5000 -> + io:format("Hanging call ~p~n",[What]), + "" + end; +call(_,_) -> + "". + + +%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +handle_info({holder_updated, Count}, State=#state{grid=Grid}) -> + wxListCtrl:setItemCount(Grid, Count), + Count > 0 andalso wxListCtrl:refreshItems(Grid, 0, Count-1), + {noreply, State}; + +handle_info(active, State) -> + cdv_wx:set_status(State#state.trunc_warn), + {noreply, State}; + +handle_info(Info, State) -> + io:format("~p:~p, Unexpected info: ~p~n", [?MODULE, ?LINE, Info]), + {noreply, State}. + +terminate(_Reason, #state{holder=Holder}) -> + Holder ! stop, + ok. + +code_change(_, _, State) -> + {ok, State}. + +handle_call(new_dump, _From, + #state{grid=Grid,detail_wins=Opened, + holder=Holder,callback=Callback}=State) -> + lists:foreach(fun({_Id, IW}) -> wxFrame:destroy(IW) end, Opened), + wxListCtrl:deleteAllItems(Grid), + Ref = erlang:monitor(process,Holder), + Holder ! stop, + receive {'DOWN',Ref,_,_,_} -> ok end, + {NewHolder,TW} = spawn_table_holder(Callback, all), + {reply, ok, State#state{detail_wins=[],holder=NewHolder,trunc_warn=TW}}; + +handle_call(Msg, _From, State) -> + io:format("~p:~p: Unhandled call ~p~n",[?MODULE, ?LINE, Msg]), + {reply, ok, State}. + +handle_cast({start_detail_win,Id}, State) -> + State2 = do_start_detail_win(Id, State), + {noreply, State2}; + +handle_cast({detail_win_closed, Id},#state{detail_wins=Opened}=State) -> + Opened2 = lists:keydelete(Id, 1, Opened), + {noreply, State#state{detail_wins=Opened2}}; + +handle_cast(Msg, State) -> + io:format("~p:~p: Unhandled cast ~p~n", [?MODULE, ?LINE, Msg]), + {noreply, State}. + +%%%%%%%%%%%%%%%%%%%%LOOP%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +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); + false -> + ok + end, + {noreply, State}; + +handle_event(#wx{event=#wxSize{size={W,_}}}, + #state{grid=Grid}=State) -> + observer_lib:set_listctrl_col_size(Grid, W), + {noreply, State}; + +handle_event(#wx{event=#wxList{type=command_list_item_right_click, + itemIndex=Row}}, + #state{panel=Panel, holder=Holder, menu_cols=MenuCols} = State) -> + Menu = wxMenu:new(), + MenuItems = + lists:flatmap( + fun(Col) -> + MenuId = ?ID_DETAILS + Col, + ColText = call(Holder, {get_row, self(), Row, Col}), + case ColText of + "[]" -> []; + _ -> + What = + case catch list_to_integer(ColText) of + NodeId when is_integer(NodeId) -> + "node " ++ ColText; + _ -> + ColText + end, + Text = "Properties for " ++ What, + wxMenu:append(Menu, MenuId, Text), + [{MenuId,ColText}] + end + end, + MenuCols), + wxWindow:popupMenu(Panel, Menu), + wxMenu:destroy(Menu), + {noreply,State#state{menu_items=MenuItems}}; + +handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}}, + #state{holder=Holder}=State) -> + Holder ! {change_sort, Col}, + {noreply, State}; + +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), + {noreply, State}; + +handle_event(Event, State) -> + io:format("~p:~p: handle event ~p\n", [?MODULE, ?LINE, Event]), + {noreply, State}. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%TABLE HOLDER%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +spawn_table_holder(Callback, Owner) -> + {Info,TW} = Callback:get_info(Owner), + Attrs = observer_lib:create_attrs(), + Parent = self(), + Holder = + case Owner of + all -> + Name = list_to_atom(atom_to_list(Callback) ++ "__holder"), + spawn_link( + fun() -> + register(Name,self()), + init_table_holder(Parent, Attrs, Callback, Info) + end), + Name; + _ -> + spawn_link( + fun() -> + init_table_holder(Parent, Attrs, Callback, Info) + end) + end, + {Holder,TW}. + +init_table_holder(Parent, Attrs, Callback, InfoList0) -> + Sort = #sort{sort_key=Callback:col_to_elem(id)}, + {_Sort, InfoList} = do_sort(Sort,InfoList0), + Info = array:from_list(InfoList), + NRows = array:size(Info), + Parent ! {holder_updated, NRows}, + table_holder(#holder{parent=Parent, + info=Info, + sort=Sort, + attrs=Attrs, + callback=Callback}). + +table_holder(#holder{callback=Callback, attrs=Attrs}=S0) -> + receive + _M={get_row, From, Row, Col} -> + %% erlang:display(_M), + State = get_row(From, Row, Col, S0), + table_holder(State); + _M={get_attr, From, Row} -> + %% erlang:display(_M), + get_attr(From, Row, Attrs), + table_holder(S0); + _M={change_sort, Col} -> + %% erlang:display(_M), + State = change_sort(Callback:col_to_elem(Col), S0), + table_holder(State); + stop -> + ok; + What -> + io:format("Table holder got ~p~n",[What]), + table_holder(S0) + end. + +change_sort(Col, S0=#holder{parent=Parent, info=Info0, sort=Sort0}) -> + NRows = array:size(Info0), + InfoList0 = array:to_list(Info0), + {Sort, InfoList}=sort(Col, Sort0, InfoList0), + Info = array:from_list(InfoList), + Parent ! {holder_updated, NRows}, + S0#holder{info=Info, last_row=undefined, sort=Sort}. + +sort(Col, Opt=#sort{sort_key=Col, sort_incr=Bool}, Table) -> + do_sort(Opt#sort{sort_incr=not Bool}, Table); +sort(Col, Sort,Table) -> + do_sort(Sort#sort{sort_key=Col, sort_incr=true}, Table). + +do_sort(Sort=#sort{sort_key=Col, sort_incr=true}, Table) -> + {Sort, lists:keysort(Col, Table)}; +do_sort(Sort=#sort{sort_key=Col, sort_incr=false}, Table) -> + {Sort, lists:reverse(lists:keysort(Col, Table))}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_cell_data(Callback, ColNo, RowInfo) -> + case element(Callback:col_to_elem(ColNo), RowInfo) of + undefined -> ""; + Cell -> try Callback:format(Cell) catch error:undef -> Cell end + end. + +get_row(From, Row, Col, + #holder{callback=Callback, last_row={Row,RowInfo}}=State) -> + Data = get_cell_data(Callback, Col, RowInfo), + From ! {self(), observer_lib:to_str(Data)}, + State; +get_row(From, Row, Col, #holder{callback=Callback, info=Info}=S0) -> + {Data,State} = + case Row >= array:size(Info) of + true -> + {"",S0}; + false -> + RowInfo = array:get(Row, Info), + CellData = get_cell_data(Callback, Col, RowInfo), + {CellData,S0#holder{last_row={Row,RowInfo}}} + end, + From ! {self(), observer_lib:to_str(Data)}, + State. + +get_attr(From, Row, Attrs) -> + Attribute = case Row rem 2 =:= 0 of + true -> Attrs#attrs.even; + false -> Attrs#attrs.odd + end, + From ! {self(), Attribute}. diff --git a/lib/observer/src/cdv_wx.erl b/lib/observer/src/cdv_wx.erl new file mode 100644 index 0000000000..26df60b0a6 --- /dev/null +++ b/lib/observer/src/cdv_wx.erl @@ -0,0 +1,462 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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_wx). +-compile(export_all). +-behaviour(wx_object). + +-export([start/1]). +-export([get_attrib/1, set_status/1, create_txt_dialog/4]). + +-export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, + handle_call/3, handle_info/2, check_page_title/1]). + +%% Includes +-include_lib("wx/include/wx.hrl"). +-include_lib("kernel/include/file.hrl"). + +-include("observer_defs.hrl"). + +%% Defines + +-define(SERVER, cdv_wx). + +-define(ID_UG, 1). +-define(ID_HOWTO, 2). +-define(ID_NOTEBOOK, 3). + +-define(GEN_STR, "General"). +-define(PRO_STR, "Processes"). +-define(PORT_STR, "Ports"). +-define(ETS_STR, "ETS Tables"). +-define(TIMER_STR, "Timers"). +-define(FUN_STR, "Funs"). +-define(ATOM_STR, "Atoms"). +-define(DIST_STR, "Nodes"). +-define(MOD_STR, "Modules"). +-define(MEM_STR, "Memory"). +-define(INT_STR, "Internal Tables"). + +%% Records +-record(state, + {server, + file, + frame, + menubar, + menus = [], + status_bar, + notebook, + main_panel, + gen_panel, + pro_panel, + port_panel, + ets_panel, + timer_panel, + fun_panel, + atom_panel, + dist_panel, + mod_panel, + mem_panel, + int_panel, + active_tab + }). + +start(File) -> + case wx_object:start(?MODULE, File, []) of + Err = {error, _} -> Err; + _Obj -> ok + end. + +get_attrib(What) -> + wx_object:call(?SERVER, {get_attrib, What}). + +set_status(What) -> + wx_object:cast(?SERVER, {status_bar, What}). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init(File0) -> + register(?SERVER, self()), + wx:new(), + + {ok,CdvServer} = crashdump_viewer:start_link(), + + catch wxSystemOptions:setOption("mac.listctrl.always_use_generic", 1), + Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Crashdump Viewer", + [{size, {850, 600}}, {style, ?wxDEFAULT_FRAME_STYLE}]), + IconFile = filename:join(code:priv_dir(observer), "erlang_observer.png"), + Icon = wxIcon:new(IconFile, [{type,?wxBITMAP_TYPE_PNG}]), + wxFrame:setIcon(Frame, Icon), + wxIcon:destroy(Icon), + + %% Setup panels + Panel = wxPanel:new(Frame, []), + Notebook = wxNotebook:new(Panel, ?ID_NOTEBOOK, [{style, ?wxBK_DEFAULT}]), + + %% Setup "statusbar" to show warnings + StatusBar = observer_lib:create_status_bar(Panel), + + %% Setup sizer create early to get it when window shows + MainSizer = wxBoxSizer:new(?wxVERTICAL), + + wxSizer:add(MainSizer, Notebook, [{proportion, 1}, {flag, ?wxEXPAND}]), + wxSizer:add(MainSizer, StatusBar, [{flag, ?wxEXPAND bor ?wxALL}, + {proportion, 0}, + {border,4}]), + wxPanel:setSizer(Panel, MainSizer), + + wxNotebook:connect(Notebook, command_notebook_page_changing), + wxFrame:connect(Frame, close_window, [{skip, true}]), + wxMenu:connect(Frame, command_menu_selected), + + case load_dump(Frame,File0) of + {ok,File} -> + %% Set window title + T1 = "Crashdump Viewer: ", + Title = + if length(File) > 70 -> + T1 ++ filename:basename(File); + true -> + T1 ++ File + end, + wxFrame:setTitle(Frame, Title), + + setup(#state{server=CdvServer, + file=File, + frame=Frame, + status_bar=StatusBar, + notebook=Notebook, + main_panel=Panel}); + error -> + wxFrame:destroy(Frame), + wx:destroy(), + crashdump_viewer:stop(), + ignore + end. + +setup(#state{frame=Frame, notebook=Notebook}=State) -> + + %% Setup Menubar & Menus + MenuBar = wxMenuBar:new(), + DefMenus = default_menus(), + observer_lib:create_menus(DefMenus, MenuBar, default), + wxFrame:setMenuBar(Frame, MenuBar), + + %% General information Panel + GenPanel = add_page(Notebook, ?GEN_STR, cdv_info_wx, cdv_gen_cb), + + %% Process Panel + ProPanel = add_page(Notebook, ?PRO_STR, cdv_virtual_list_wx, cdv_proc_cb), + + %% Port Panel + PortPanel = add_page(Notebook, ?PORT_STR, cdv_virtual_list_wx, cdv_port_cb), + + %% Table Panel + EtsPanel = add_page(Notebook, ?ETS_STR, cdv_virtual_list_wx, cdv_ets_cb), + + %% Timer Panel + TimerPanel = add_page(Notebook, ?TIMER_STR, cdv_virtual_list_wx,cdv_timer_cb), + + %% Fun Panel + FunPanel = add_page(Notebook, ?FUN_STR, cdv_virtual_list_wx, cdv_fun_cb), + + %% Atom Panel + AtomPanel = add_page(Notebook, ?ATOM_STR, cdv_virtual_list_wx, cdv_atom_cb), + + %% Distribution Panel + DistPanel = add_page(Notebook, ?DIST_STR, cdv_virtual_list_wx, cdv_dist_cb), + + %% Loaded Modules Panel + ModPanel = add_page(Notebook, ?MOD_STR, cdv_virtual_list_wx, cdv_mod_cb), + + %% Memory Panel + MemPanel = add_page(Notebook, ?MEM_STR, cdv_multi_wx, cdv_mem_cb), + + %% Memory Panel + IntPanel = add_page(Notebook, ?INT_STR, cdv_multi_wx, cdv_int_tab_cb), + + %% Show the window + wxFrame:show(Frame), + + GenPid = wx_object:get_pid(GenPanel), + GenPid ! active, + observer_lib:destroy_progress_dialog(), + process_flag(trap_exit, true), + {Frame, State#state{menubar = MenuBar, + gen_panel = GenPanel, + pro_panel = ProPanel, + port_panel = PortPanel, + ets_panel = EtsPanel, + timer_panel = TimerPanel, + fun_panel = FunPanel, + atom_panel = AtomPanel, + dist_panel = DistPanel, + mod_panel = ModPanel, + mem_panel = MemPanel, + int_panel = IntPanel, + active_tab = GenPid + }}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%Callbacks +handle_event(#wx{event=#wxNotebook{type=command_notebook_page_changing}}, + #state{active_tab=Previous} = State) -> + case get_active_pid(State) of + Previous -> {noreply, State}; + Pid -> + Pid ! active, + {noreply, State#state{active_tab=Pid}} + end; + +handle_event(#wx{event = #wxClose{}}, State) -> + {stop, normal, State}; + +handle_event(#wx{id = ?wxID_OPEN, + event = #wxCommand{type = command_menu_selected}}, + State) -> + NewState = + case load_dump(State#state.frame,undefined) of + {ok,File} -> + Panels = [State#state.gen_panel, + State#state.pro_panel, + State#state.port_panel, + State#state.ets_panel, + State#state.timer_panel, + State#state.fun_panel, + State#state.atom_panel, + State#state.dist_panel, + State#state.mod_panel, + State#state.mem_panel, + State#state.int_panel], + _ = [wx_object:call(Panel,new_dump) || Panel<-Panels], + wxNotebook:setSelection(State#state.notebook,0), + observer_lib:destroy_progress_dialog(), + State#state{file=File}; + error -> + State + end, + {noreply,NewState}; + +handle_event(#wx{id = ?wxID_EXIT, + event = #wxCommand{type = command_menu_selected}}, + State) -> + {stop, normal, State}; + +handle_event(#wx{id = HelpId, + event = #wxCommand{type = command_menu_selected}}, + State) when HelpId==?wxID_HELP; HelpId==?ID_UG; HelpId==?ID_HOWTO -> + Help = get_help_doc(HelpId), + wx_misc:launchDefaultBrowser(Help) orelse + create_txt_dialog(State#state.frame, + "Could not launch browser: ~n " ++ Help, + "Error", ?wxICON_ERROR), + {noreply, State}; + +handle_event(#wx{id = ?wxID_ABOUT, + event = #wxCommand{type = command_menu_selected}}, + State = #state{frame=Frame}) -> + AboutString = "Display information from an erlang crash dump", + Style = [{style, ?wxOK bor ?wxSTAY_ON_TOP}, + {caption, "About"}], + wxMessageDialog:showModal(wxMessageDialog:new(Frame, AboutString, Style)), + {noreply, State}; + +handle_event(Event, State) -> + Pid = get_active_pid(State), + Pid ! Event, + {noreply, State}. + +handle_cast({status_bar, Msg}, State=#state{status_bar=SB}) -> + wxTextCtrl:clear(SB), + wxTextCtrl:writeText(SB, Msg), + {noreply, State}; + +handle_cast(_Cast, State) -> + {noreply, State}. + +handle_call({get_attrib, Attrib}, _From, State) -> + {reply, get(Attrib), State}; + +handle_call(_Msg, _From, State) -> + {reply, ok, State}. + +handle_info({'EXIT', Pid, normal}, #state{server=Pid}=State) -> + {stop, normal, State}; + +handle_info({'EXIT', Pid, _Reason}, State) -> + io:format("Child (~s) crashed exiting: ~p ~p~n", + [pid2panel(Pid, State), Pid,_Reason]), + {stop, normal, State}; + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, #state{frame = Frame}) -> + wxFrame:destroy(Frame), + wx:destroy(), + crashdump_viewer:stop(), + ok. + +code_change(_, _, State) -> + {ok, State}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +add_page(Notebook,Title,Callback,Extra) -> + Panel = Callback:start_link(Notebook, Extra), + wxNotebook:addPage(Notebook, Panel, Title, []), + Panel. + +create_txt_dialog(Frame, Msg, Title, Style) -> + MD = wxMessageDialog:new(Frame, Msg, [{style, Style}]), + wxMessageDialog:setTitle(MD, Title), + wxDialog:showModal(MD), + wxDialog:destroy(MD). + +check_page_title(Notebook) -> + Selection = wxNotebook:getSelection(Notebook), + wxNotebook:getPageText(Notebook, Selection). + +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 + }) -> + Panel = case check_page_title(Notebook) of + ?GEN_STR -> Gen; + ?PRO_STR -> Pro; + ?PORT_STR -> Ports; + ?ETS_STR -> Ets; + ?TIMER_STR -> Timers; + ?FUN_STR -> Funs; + ?ATOM_STR -> Atoms; + ?DIST_STR -> Dist; + ?MOD_STR -> Mods; + ?MEM_STR -> Mem; + ?INT_STR -> Int + end, + wx_object:get_pid(Panel). + +pid2panel(Pid, #state{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}) -> + case Pid of + Gen -> ?GEN_STR; + Pro -> ?PRO_STR; + Ports -> ?PORT_STR; + Ets -> ?ETS_STR; + Timers -> ?TIMER_STR; + Funs -> ?FUN_STR; + Atoms -> ?ATOM_STR; + Dist -> ?DIST_STR; + Mods -> ?MOD_STR; + Mem -> ?MEM_STR; + Int -> ?INT_STR; + _ -> "unknown" + end. + +default_menus() -> + Open = #create_menu{id = ?wxID_OPEN, text = "Open new crash dump"}, + Quit = #create_menu{id = ?wxID_EXIT, text = "Quit"}, + About = #create_menu{id = ?wxID_ABOUT, text = "About"}, + Help = #create_menu{id = ?wxID_HELP}, + UG = #create_menu{id = ?ID_UG, text = "Crashdump viewer user's guide"}, + Howto = #create_menu{id = ?ID_HOWTO, text = "How to interpret crash dump"}, + case os:type() =:= {unix, darwin} of + false -> + FileMenu = {"File", [Open,Quit]}, + HelpMenu = {"Help", [About,Help,UG,Howto]}, + [FileMenu, 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. + [{"File", [Open, About,Quit]}, {"&Help", [Help,UG,Howto]}] + end. + + +load_dump(Frame,undefined) -> + FD = wxFileDialog:new(wx:null(), + [{style,?wxFD_OPEN bor ?wxFD_FILE_MUST_EXIST}]), + case wxFileDialog:showModal(FD) of + ?wxID_OK -> + Path = wxFileDialog:getPath(FD), + wxDialog:destroy(FD), + load_dump(Frame,Path); + _ -> + wxDialog:destroy(FD), + error + end; +load_dump(Frame,FileName) -> + ok = observer_lib:display_progress_dialog("Crashdump Viewer", + "Loading crashdump"), + crashdump_viewer:read_file(FileName), + case observer_lib:wait_for_progress() of + ok -> + %% Set window title + T1 = "Crashdump Viewer: ", + Title = + if length(FileName) > 70 -> + T1 ++ filename:basename(FileName); + true -> + T1 ++ FileName + end, + wxFrame:setTitle(Frame, Title), + {ok,FileName}; + error -> + error + end. + +%%%----------------------------------------------------------------- +%%% Find help document (HTML files) +get_help_doc(HelpId) -> + Internal = get_internal_help_doc(HelpId), + case filelib:is_file(Internal) of + true -> Internal; + false -> get_external_help_doc(HelpId) + end. + +get_internal_help_doc(?ID_HOWTO) -> + filename:join(erts_doc_dir(),help_file(?ID_HOWTO)); +get_internal_help_doc(HelpId) -> + filename:join(observer_doc_dir(),help_file(HelpId)). + +get_external_help_doc(?ID_HOWTO) -> + filename:join("http://www.erlang.org/doc/apps/erts",help_file(?ID_HOWTO)); +get_external_help_doc(HelpId) -> + filename:join("http://www.erlang.org/doc/apps/observer",help_file(HelpId)). + +observer_doc_dir() -> + filename:join([code:lib_dir(observer),"doc","html"]). + +erts_doc_dir() -> + ErtsVsn = erlang:system_info(version), + RootDir = code:root_dir(), + VsnErtsDir = filename:join(RootDir,"erts-"++ErtsVsn), + DocDir = filename:join(["doc","html"]), + case filelib:is_dir(VsnErtsDir) of + true -> + filename:join(VsnErtsDir,DocDir); + false -> + %% So this can be run in source tree + filename:join([RootDir,"erts",DocDir]) + end. + +help_file(?wxID_HELP) -> "crashdump_help.html"; +help_file(?ID_UG) -> "crashdump_ug.html"; +help_file(?ID_HOWTO) -> "crash_dump.html". diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl index 53e0241711..a17efbccb0 100644 --- a/lib/observer/src/crashdump_viewer.erl +++ b/lib/observer/src/crashdump_viewer.erl @@ -153,7 +153,7 @@ stop_debug() -> start() -> start(undefined). start(File) -> - crashdump_viewer_wx:start(File). + cdv_wx:start(File). stop() -> case whereis(?SERVER) of @@ -216,7 +216,7 @@ usage() -> %% External functions %%==================================================================== %%%-------------------------------------------------------------------- -%%% Start the server - called by crashdump_viewer_wx +%%% Start the server - called by cdv_wx start_link() -> case whereis(?SERVER) of undefined -> @@ -226,7 +226,7 @@ start_link() -> end. %%%----------------------------------------------------------------- -%%% Called by crashdump_viewer_wx +%%% Called by cdv_wx read_file(File) -> cast({read_file,File}). @@ -1378,7 +1378,7 @@ get_ports(File) -> lookup_and_parse_index(File,?port,ParseFun,"ports"). %% Converting port string to tuple to secure correct sorting. This is -%% converted back in cdv_port_wx:format/1. +%% converted back in cdv_port_cb:format/1. port_to_tuple("#Port<"++Port) -> [I1,I2] = string:tokens(Port,".>"), {list_to_integer(I1),list_to_integer(I2)}. @@ -1790,7 +1790,7 @@ get_atoms1(Fd,[Bin|Bins],N,Atoms) -> %% This ensures sorting according to first actual letter in the atom, %% disregarding possible single quote. It is formatted back to correct -%% syntax in cdv_atom_wx:format/1 +%% syntax in cdv_atom_cb:format/1 get_atom(<<"\'",Atom/binary>>) -> {Atom,q}; % quoted get_atom(Atom) when is_binary(Atom) -> diff --git a/lib/observer/src/crashdump_viewer_html.erl b/lib/observer/src/crashdump_viewer_html.erl deleted file mode 100644 index 4b7caaf41e..0000000000 --- a/lib/observer/src/crashdump_viewer_html.erl +++ /dev/null @@ -1,388 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% 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 -%% 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(crashdump_viewer_html). - -%% -%% This module implements the HTML generation for the crashdump -%% viewer. No logic or states are kept by this module. -%% - --export([plain_page/1, - expandable_term/3, - warning/1]). - --include("crashdump_viewer.hrl"). --include("observer_defs.hrl"). - -%%%----------------------------------------------------------------- -%%% Display the given information as is, no heading -%%% Empty body if no info exists. -warning(Info) -> - header(body(warning_body(Info))). - -warning_body(Info) -> - [warn(Info)]. - -%%%----------------------------------------------------------------- -%%% Display the given information as is, no heading -%%% Empty body if no info exists. -plain_page(Info) -> - header(body(plain_body(Info))). - -plain_body(Info) -> - [pre(href_proc_port(lists:flatten(Info)))]. - -%%%----------------------------------------------------------------- -%%% Expanded memory -expandable_term(Heading,Expanded,Tab) -> - header(Heading,body(expandable_term_body(Heading,Expanded,Tab))). - -expandable_term_body(Heading,[],_Tab) -> - [case Heading of - "MsgQueue" -> "No messages were found"; - "Message Queue" -> "No messages were found"; - "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." - end]; -expandable_term_body(Heading,Expanded,Tab) -> - Attr = "BORDER=0 CELLPADDING=0 CELLSPACING=1 WIDTH=100%", - [case Heading of - "MsgQueue" -> - table(Attr, - [tr( - [th("WIDTH=70%","Message"), - th("WIDTH=30%","SeqTraceToken")]) | - element(1, lists:mapfoldl(fun(Msg, Even) -> - {msgq_table(Tab, Msg, Even), - not Even} - end, - true, Expanded))]); - "Message Queue" -> - table(Attr, - [tr( - [th("WIDTH=10%","Id"), - th("WIDTH=90%","Message")]) | - element(1, lists:mapfoldl(fun(Msg, {Even,N}) -> - {msgq_table(Tab, Msg, N, Even), - {not Even, N+1}} - end, - {true,1}, Expanded))]); - "StackDump" -> - table(Attr, - [tr( - [th("WIDTH=20%","Label"), - th("WIDTH=80%","Term")]) | - element(1, lists:mapfoldl(fun(Entry, Even) -> - {stackdump_table(Tab, Entry, Even), - not Even} - end, true, Expanded))]); - "ProcState" -> - table(Attr, - [tr( - [th("WIDTH=20%","Label"), - th("WIDTH=80%","Information")]) | - element(1, lists:mapfoldl(fun(Entry, Even) -> - {proc_state(Tab, Entry,Even), - not Even} - end, true, Expanded))]); - _ -> - table(Attr, - [tr( - [th("WIDTH=30%","Key"), - th("WIDTH=70%","Value")]) | - element(1, lists:mapfoldl(fun(Entry, Even) -> - {dict_table(Tab, Entry,Even), - not Even} - end, true, Expanded))]) - end]. - -msgq_table(Tab,{Msg0,Token0}, Even) -> - Token = case Token0 of - [] -> ""; - _ -> io_lib:fwrite("~w",[Token0]) - end, - Msg = all_or_expand(Tab,Msg0), - tr(color(Even),[td(pre(Msg)), td(Token)]). - -msgq_table(Tab,Msg0, Id, Even) -> - Msg = all_or_expand(Tab,Msg0), - tr(color(Even),[td(integer_to_list(Id)), td(pre(Msg))]). - -stackdump_table(Tab,{Label0,Term0},Even) -> - Label = io_lib:format("~w",[Label0]), - Term = all_or_expand(Tab,Term0), - tr(color(Even), [td("VALIGN=center",pre(Label)), td(pre(Term))]). - -dict_table(Tab,{Key0,Value0}, Even) -> - Key = all_or_expand(Tab,Key0), - Value = all_or_expand(Tab,Value0), - tr(color(Even), [td("VALIGN=center",pre(Key)), td(pre(Value))]). - -proc_state(Tab,{Key0,Value0}, Even) -> - Key = lists:flatten(io_lib:format("~s",[Key0])), - Value = all_or_expand(Tab,Value0), - tr(color(Even), [td("VALIGN=center",Key), td(pre(Value))]). - -all_or_expand(Tab,Term) -> - Preview = io_lib:format("~P",[Term,8]), - Check = io_lib:format("~P",[Term,100]), - Exp = Preview=/=Check, - all_or_expand(Tab,Term,Preview,Exp). -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(), - ets:insert(Tab,{Key,Term}), - [href_proc_port(lists:flatten(Preview), false), $\n, - href("TARGET=\"expanded\"", - ["#Term?key1="++integer_to_list(Key1)++ - "&key2="++integer_to_list(Key2)++ - "&key3="++integer_to_list(Key3)], - "Click to expand above term")]; -all_or_expand(Tab,Bin,PreviewStr,true) when is_binary(Bin) -> - <> = Bin, - Size = byte_size(Bin), - Hash = erlang:phash2(Bin), - Key = {Preview, Size, Hash}, - ets:insert(Tab,{Key,Bin}), - [href_proc_port(lists:flatten(PreviewStr), false), $\n, - href("TARGET=\"expanded\"", - ["#OBSBinary?key1="++integer_to_list(Preview)++ - "&key2="++integer_to_list(Size)++ - "&key3="++integer_to_list(Hash)], - "Click to expand above term")]. - - - -color(true) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_EVEN)); -color(false) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_ODD)). - -%%%----------------------------------------------------------------- -%%% Internal library -start_html() -> - "\n". -stop_html() -> - "". -start_html_body() -> - "\n". -stop_html_body() -> - "\n". - -header(Body) -> - header("","",Body). -header(Title,Body) -> - header(Title,"",Body). -header(Title,JavaScript,Body) -> - [%only_http_header(), - html_header(Title,JavaScript,Body)]. - -html_header(Title,JavaScript,Body) -> - [start_html(), - only_html_header(Title,JavaScript), - Body, - stop_html()]. - -only_html_header(Title,JavaScript) -> - ["\n", - "", Title, "\n", - JavaScript, - "\n"]. - -body(Text) -> - [start_html_body(), - Text, - stop_html_body()]. - -start_table(Args) -> - ["\n"]. -stop_table() -> - "
\n". - -table(Args,Text) -> - [start_table(Args), Text, stop_table()]. -tr(Text) -> - ["\n", Text, "\n\n"]. -tr(Args,Text) -> - ["\n", Text, "\n\n"]. -th(Args,Text) -> - ["\n", Text, "\n\n"]. -td(Text) -> - ["", Text, ""]. -td(Args,Text) -> - ["", Text, ""]. - -start_pre() -> - "
".
-stop_pre() ->
-    "
". -pre(Text) -> - [start_pre(),Text,stop_pre()]. -href(Link,Text) -> - ["",Text,""]. -href(Args,Link,Text) -> - ["",Text,""]. -font(Args,Text) -> - ["\n",Text,"\n\n"]. -p(Text) -> - ["

",Text,"

\n"]. -br() -> - "
\n". - - -%% In all the following, "<" is changed to "<" and ">" is changed to ">" -href_proc_port(Text) -> - href_proc_port(Text,true). -href_proc_port(Text,LinkToBin) -> - href_proc_port(Text,[],LinkToBin). -href_proc_port("#Ref<"++T,Acc,LTB) -> - %% No links to refs - href_proc_port(T,["#Ref<"|Acc],LTB); -href_proc_port("#Fun<"++T,Acc,LTB) -> - %% No links to funs - href_proc_port(T,["#Fun<"|Acc],LTB); -href_proc_port("#Port<"++T,Acc,LTB) -> - {Port0,Rest} = split($>,T), - Port = "#Port<"++Port0 ++ ">", - href_proc_port(Rest,[href(Port,Port)|Acc],LTB); -href_proc_port("<<"++T,Acc,LTB) -> - %% No links to binaries - href_proc_port(T,["<<"|Acc],LTB); -href_proc_port("<"++([C|_]=T),Acc,LTB) when $0 =< C, C =< $9 -> - %% Pid - {Pid0,Rest} = split($>,T), - Pid = "<" ++ Pid0 ++ ">", - href_proc_port(Rest,[href(Pid,Pid)|Acc],LTB); -href_proc_port("['#CDVBin'"++T,Acc,LTB) -> - %% Binary written by crashdump_viewer:parse_heap_term(...) - href_proc_bin(cdv, T, Acc, LTB); -href_proc_port("['#OBSBin'"++T,Acc,LTB) -> - %% Binary written by crashdump_viewer:parse_heap_term(...) - href_proc_bin(obs, T, Acc, LTB); -href_proc_port("['#CDVPort'"++T,Acc,LTB) -> - %% Port written by crashdump_viewer:parse_term(...) - {Port0,Rest} = split($],T), - PortStr= - case string:tokens(Port0,",.|") of - [X,Y] -> - Port = "#Port<"++X++"."++Y++">", - href(Port,Port); - Ns -> - "#Port<" ++ string:join(Ns,".") ++"...>" - end, - href_proc_port(Rest,[PortStr|Acc],LTB); -href_proc_port("['#CDVPid'"++T,Acc,LTB) -> - %% Pid written by crashdump_viewer:parse_term(...) - {Pid0,Rest} = split($],T), - PidStr = - case string:tokens(Pid0,",.|") of - [X,Y,Z] -> - Pid = "<"++X++"."++Y++"."++Z++">", - href(Pid,Pid); - Ns -> - "<" ++ string:join(Ns,".") ++ "...>" - end, - href_proc_port(Rest,[PidStr|Acc],LTB); -href_proc_port("'#CDVIncompleteHeap'"++T,Acc,LTB)-> - %% The heap is incomplete! Written by crashdump_viewer:deref_pts(...) - IH = lists:reverse( - lists:flatten( - "...(Incomplete Heap)")), - href_proc_port(T,IH++Acc,LTB); -href_proc_port("'#CDVTruncatedBinary'"++T,Acc,LTB)-> - %% A binary which is truncated! Written by - %% crashdump_viewer:parse_heap_term(...) - IH = lists:reverse( - lists:flatten( - "<<...(Truncated Binary)>>" - "")), - href_proc_port(T,IH++Acc,LTB); -href_proc_port("'#CDVNonexistingBinary'"++T,Acc,LTB)-> - %% A binary which could not be found in the dump! Written by - %% crashdump_viewer:parse_heap_term(...) - IH = lists:reverse( - lists:flatten( - "<<...(Nonexisting Binary)>>" - "")), - href_proc_port(T,IH++Acc,LTB); -href_proc_port("<"++T,Acc,LTB) -> - href_proc_port(T,["<"|Acc],LTB); -href_proc_port(">"++T,Acc,LTB) -> - href_proc_port(T,[">"|Acc],LTB); -href_proc_port([H|T],Acc,LTB) -> - href_proc_port(T,[H|Acc],LTB); -href_proc_port([],Acc,_) -> - lists:reverse(Acc). - -href_proc_bin(From, T, Acc, LTB) -> - {OffsetSizePos,Rest} = split($],T), - BinStr = - case string:tokens(OffsetSizePos,",.| \n") of - [Offset,Size,Pos] when From =:= cdv -> - Id = {list_to_integer(Offset),10,list_to_integer(Pos)}, - {ok,PreviewBin} = crashdump_viewer:expand_binary(Id), - PreviewStr = ["<<", - [integer_to_list(X)++"," || <> <= PreviewBin], - "...(", - observer_lib:to_str({bytes,Size}), - ")>>"], - if LTB -> - href("TARGET=\"expanded\"", - ["#Binary?offset="++Offset++ - "&size="++Size++ - "&pos="++Pos], - PreviewStr); - true -> - PreviewStr - end; - [Preview,Size,Md5] when From =:= obs -> - PreviewBin = <<(list_to_integer(Preview)):80>>, - PreviewStr = ["<<", - [integer_to_list(X)++"," || <> <= PreviewBin], - "...(", - observer_lib:to_str({bytes,list_to_integer(Size)}), - ")>>"], - if LTB -> - href("TARGET=\"expanded\"", - ["#OBSBinary?key1="++Preview++ - "&key2="++Size++ - "&key3="++Md5], - PreviewStr); - true -> - PreviewStr - end; - _ -> - "<< ... >>" - end, - href_proc_port(Rest,[BinStr|Acc],LTB). - -split(Char,Str) -> - split(Char,Str,[]). -split(Char,[Char|Str],Acc) -> % match Char - {lists:reverse(Acc),Str}; -split(Char,[H|T],Acc) -> - split(Char,T,[H|Acc]). - - -warn([]) -> - []; -warn(Warning) -> - font("COLOR=\"#FF0000\"",p([Warning,br(),br()])). diff --git a/lib/observer/src/crashdump_viewer_wx.erl b/lib/observer/src/crashdump_viewer_wx.erl deleted file mode 100644 index 3464dfafae..0000000000 --- a/lib/observer/src/crashdump_viewer_wx.erl +++ /dev/null @@ -1,462 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 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 -%% 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(crashdump_viewer_wx). --compile(export_all). --behaviour(wx_object). - --export([start/1]). --export([get_attrib/1, set_status/1, create_txt_dialog/4]). - --export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, - handle_call/3, handle_info/2, check_page_title/1]). - -%% Includes --include_lib("wx/include/wx.hrl"). --include_lib("kernel/include/file.hrl"). - --include("observer_defs.hrl"). - -%% Defines - --define(SERVER, cdv_wx). - --define(ID_UG, 1). --define(ID_HOWTO, 2). --define(ID_NOTEBOOK, 3). - --define(GEN_STR, "General"). --define(PRO_STR, "Processes"). --define(PORT_STR, "Ports"). --define(ETS_STR, "ETS Tables"). --define(TIMER_STR, "Timers"). --define(FUN_STR, "Funs"). --define(ATOM_STR, "Atoms"). --define(DIST_STR, "Nodes"). --define(MOD_STR, "Modules"). --define(MEM_STR, "Memory"). --define(INT_STR, "Internal Tables"). - -%% Records --record(state, - {server, - file, - frame, - menubar, - menus = [], - status_bar, - notebook, - main_panel, - gen_panel, - pro_panel, - port_panel, - ets_panel, - timer_panel, - fun_panel, - atom_panel, - dist_panel, - mod_panel, - mem_panel, - int_panel, - active_tab - }). - -start(File) -> - case wx_object:start(?MODULE, File, []) of - Err = {error, _} -> Err; - _Obj -> ok - end. - -get_attrib(What) -> - wx_object:call(?SERVER, {get_attrib, What}). - -set_status(What) -> - wx_object:cast(?SERVER, {status_bar, What}). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -init(File0) -> - register(?SERVER, self()), - wx:new(), - - {ok,CdvServer} = crashdump_viewer:start_link(), - - catch wxSystemOptions:setOption("mac.listctrl.always_use_generic", 1), - Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Crashdump Viewer", - [{size, {850, 600}}, {style, ?wxDEFAULT_FRAME_STYLE}]), - IconFile = filename:join(code:priv_dir(observer), "erlang_observer.png"), - Icon = wxIcon:new(IconFile, [{type,?wxBITMAP_TYPE_PNG}]), - wxFrame:setIcon(Frame, Icon), - wxIcon:destroy(Icon), - - %% Setup panels - Panel = wxPanel:new(Frame, []), - Notebook = wxNotebook:new(Panel, ?ID_NOTEBOOK, [{style, ?wxBK_DEFAULT}]), - - %% Setup "statusbar" to show warnings - StatusBar = observer_lib:create_status_bar(Panel), - - %% Setup sizer create early to get it when window shows - MainSizer = wxBoxSizer:new(?wxVERTICAL), - - wxSizer:add(MainSizer, Notebook, [{proportion, 1}, {flag, ?wxEXPAND}]), - wxSizer:add(MainSizer, StatusBar, [{flag, ?wxEXPAND bor ?wxALL}, - {proportion, 0}, - {border,4}]), - wxPanel:setSizer(Panel, MainSizer), - - wxNotebook:connect(Notebook, command_notebook_page_changing), - wxFrame:connect(Frame, close_window, [{skip, true}]), - wxMenu:connect(Frame, command_menu_selected), - - case load_dump(Frame,File0) of - {ok,File} -> - %% Set window title - T1 = "Crashdump Viewer: ", - Title = - if length(File) > 70 -> - T1 ++ filename:basename(File); - true -> - T1 ++ File - end, - wxFrame:setTitle(Frame, Title), - - setup(#state{server=CdvServer, - file=File, - frame=Frame, - status_bar=StatusBar, - notebook=Notebook, - main_panel=Panel}); - error -> - wxFrame:destroy(Frame), - wx:destroy(), - crashdump_viewer:stop(), - ignore - end. - -setup(#state{frame=Frame, notebook=Notebook}=State) -> - - %% Setup Menubar & Menus - MenuBar = wxMenuBar:new(), - DefMenus = default_menus(), - observer_lib:create_menus(DefMenus, MenuBar, default), - wxFrame:setMenuBar(Frame, MenuBar), - - %% General information Panel - GenPanel = add_page(Notebook, ?GEN_STR, cdv_info_page, cdv_gen_wx), - - %% Process Panel - ProPanel = add_page(Notebook, ?PRO_STR, cdv_virtual_list, cdv_proc_wx), - - %% Port Panel - PortPanel = add_page(Notebook, ?PORT_STR, cdv_virtual_list, cdv_port_wx), - - %% Table Panel - EtsPanel = add_page(Notebook, ?ETS_STR, cdv_virtual_list, cdv_ets_wx), - - %% Timer Panel - TimerPanel = add_page(Notebook, ?TIMER_STR, cdv_virtual_list, cdv_timer_wx), - - %% Fun Panel - FunPanel = add_page(Notebook, ?FUN_STR, cdv_virtual_list, cdv_fun_wx), - - %% Atom Panel - AtomPanel = add_page(Notebook, ?ATOM_STR, cdv_virtual_list, cdv_atom_wx), - - %% Distribution Panel - DistPanel = add_page(Notebook, ?DIST_STR, cdv_virtual_list, cdv_dist_wx), - - %% Loaded Modules Panel - ModPanel = add_page(Notebook, ?MOD_STR, cdv_virtual_list, cdv_mod_wx), - - %% Memory Panel - MemPanel = add_page(Notebook, ?MEM_STR, cdv_multi_panel, cdv_mem_wx), - - %% Memory Panel - IntPanel = add_page(Notebook, ?INT_STR, cdv_multi_panel, cdv_int_tab_wx), - - %% Show the window - wxFrame:show(Frame), - - GenPid = wx_object:get_pid(GenPanel), - GenPid ! active, - observer_lib:destroy_progress_dialog(), - process_flag(trap_exit, true), - {Frame, State#state{menubar = MenuBar, - gen_panel = GenPanel, - pro_panel = ProPanel, - port_panel = PortPanel, - ets_panel = EtsPanel, - timer_panel = TimerPanel, - fun_panel = FunPanel, - atom_panel = AtomPanel, - dist_panel = DistPanel, - mod_panel = ModPanel, - mem_panel = MemPanel, - int_panel = IntPanel, - active_tab = GenPid - }}. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%%Callbacks -handle_event(#wx{event=#wxNotebook{type=command_notebook_page_changing}}, - #state{active_tab=Previous} = State) -> - case get_active_pid(State) of - Previous -> {noreply, State}; - Pid -> - Pid ! active, - {noreply, State#state{active_tab=Pid}} - end; - -handle_event(#wx{event = #wxClose{}}, State) -> - {stop, normal, State}; - -handle_event(#wx{id = ?wxID_OPEN, - event = #wxCommand{type = command_menu_selected}}, - State) -> - NewState = - case load_dump(State#state.frame,undefined) of - {ok,File} -> - Panels = [State#state.gen_panel, - State#state.pro_panel, - State#state.port_panel, - State#state.ets_panel, - State#state.timer_panel, - State#state.fun_panel, - State#state.atom_panel, - State#state.dist_panel, - State#state.mod_panel, - State#state.mem_panel, - State#state.int_panel], - _ = [wx_object:call(Panel,new_dump) || Panel<-Panels], - wxNotebook:setSelection(State#state.notebook,0), - observer_lib:destroy_progress_dialog(), - State#state{file=File}; - error -> - State - end, - {noreply,NewState}; - -handle_event(#wx{id = ?wxID_EXIT, - event = #wxCommand{type = command_menu_selected}}, - State) -> - {stop, normal, State}; - -handle_event(#wx{id = HelpId, - event = #wxCommand{type = command_menu_selected}}, - State) when HelpId==?wxID_HELP; HelpId==?ID_UG; HelpId==?ID_HOWTO -> - Help = get_help_doc(HelpId), - wx_misc:launchDefaultBrowser(Help) orelse - create_txt_dialog(State#state.frame, - "Could not launch browser: ~n " ++ Help, - "Error", ?wxICON_ERROR), - {noreply, State}; - -handle_event(#wx{id = ?wxID_ABOUT, - event = #wxCommand{type = command_menu_selected}}, - State = #state{frame=Frame}) -> - AboutString = "Display information from an erlang crash dump", - Style = [{style, ?wxOK bor ?wxSTAY_ON_TOP}, - {caption, "About"}], - wxMessageDialog:showModal(wxMessageDialog:new(Frame, AboutString, Style)), - {noreply, State}; - -handle_event(Event, State) -> - Pid = get_active_pid(State), - Pid ! Event, - {noreply, State}. - -handle_cast({status_bar, Msg}, State=#state{status_bar=SB}) -> - wxTextCtrl:clear(SB), - wxTextCtrl:writeText(SB, Msg), - {noreply, State}; - -handle_cast(_Cast, State) -> - {noreply, State}. - -handle_call({get_attrib, Attrib}, _From, State) -> - {reply, get(Attrib), State}; - -handle_call(_Msg, _From, State) -> - {reply, ok, State}. - -handle_info({'EXIT', Pid, normal}, #state{server=Pid}=State) -> - {stop, normal, State}; - -handle_info({'EXIT', Pid, _Reason}, State) -> - io:format("Child (~s) crashed exiting: ~p ~p~n", - [pid2panel(Pid, State), Pid,_Reason]), - {stop, normal, State}; - -handle_info(_Info, State) -> - {noreply, State}. - -terminate(_Reason, #state{frame = Frame}) -> - wxFrame:destroy(Frame), - wx:destroy(), - crashdump_viewer:stop(), - ok. - -code_change(_, _, State) -> - {ok, State}. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -add_page(Notebook,Title,Callback,Extra) -> - Panel = Callback:start_link(Notebook, Extra), - wxNotebook:addPage(Notebook, Panel, Title, []), - Panel. - -create_txt_dialog(Frame, Msg, Title, Style) -> - MD = wxMessageDialog:new(Frame, Msg, [{style, Style}]), - wxMessageDialog:setTitle(MD, Title), - wxDialog:showModal(MD), - wxDialog:destroy(MD). - -check_page_title(Notebook) -> - Selection = wxNotebook:getSelection(Notebook), - wxNotebook:getPageText(Notebook, Selection). - -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 - }) -> - Panel = case check_page_title(Notebook) of - ?GEN_STR -> Gen; - ?PRO_STR -> Pro; - ?PORT_STR -> Ports; - ?ETS_STR -> Ets; - ?TIMER_STR -> Timers; - ?FUN_STR -> Funs; - ?ATOM_STR -> Atoms; - ?DIST_STR -> Dist; - ?MOD_STR -> Mods; - ?MEM_STR -> Mem; - ?INT_STR -> Int - end, - wx_object:get_pid(Panel). - -pid2panel(Pid, #state{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}) -> - case Pid of - Gen -> ?GEN_STR; - Pro -> ?PRO_STR; - Ports -> ?PORT_STR; - Ets -> ?ETS_STR; - Timers -> ?TIMER_STR; - Funs -> ?FUN_STR; - Atoms -> ?ATOM_STR; - Dist -> ?DIST_STR; - Mods -> ?MOD_STR; - Mem -> ?MEM_STR; - Int -> ?INT_STR; - _ -> "unknown" - end. - -default_menus() -> - Open = #create_menu{id = ?wxID_OPEN, text = "Open new crash dump"}, - Quit = #create_menu{id = ?wxID_EXIT, text = "Quit"}, - About = #create_menu{id = ?wxID_ABOUT, text = "About"}, - Help = #create_menu{id = ?wxID_HELP}, - UG = #create_menu{id = ?ID_UG, text = "Crashdump viewer user's guide"}, - Howto = #create_menu{id = ?ID_HOWTO, text = "How to interpret crash dump"}, - case os:type() =:= {unix, darwin} of - false -> - FileMenu = {"File", [Open,Quit]}, - HelpMenu = {"Help", [About,Help,UG,Howto]}, - [FileMenu, 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. - [{"File", [Open, About,Quit]}, {"&Help", [Help,UG,Howto]}] - end. - - -load_dump(Frame,undefined) -> - FD = wxFileDialog:new(wx:null(), - [{style,?wxFD_OPEN bor ?wxFD_FILE_MUST_EXIST}]), - case wxFileDialog:showModal(FD) of - ?wxID_OK -> - Path = wxFileDialog:getPath(FD), - wxDialog:destroy(FD), - load_dump(Frame,Path); - _ -> - wxDialog:destroy(FD), - error - end; -load_dump(Frame,FileName) -> - ok = observer_lib:display_progress_dialog("Crashdump Viewer", - "Loading crashdump"), - crashdump_viewer:read_file(FileName), - case observer_lib:wait_for_progress() of - ok -> - %% Set window title - T1 = "Crashdump Viewer: ", - Title = - if length(FileName) > 70 -> - T1 ++ filename:basename(FileName); - true -> - T1 ++ FileName - end, - wxFrame:setTitle(Frame, Title), - {ok,FileName}; - error -> - error - end. - -%%%----------------------------------------------------------------- -%%% Find help document (HTML files) -get_help_doc(HelpId) -> - Internal = get_internal_help_doc(HelpId), - case filelib:is_file(Internal) of - true -> Internal; - false -> get_external_help_doc(HelpId) - end. - -get_internal_help_doc(?ID_HOWTO) -> - filename:join(erts_doc_dir(),help_file(?ID_HOWTO)); -get_internal_help_doc(HelpId) -> - filename:join(observer_doc_dir(),help_file(HelpId)). - -get_external_help_doc(?ID_HOWTO) -> - filename:join("http://www.erlang.org/doc/apps/erts",help_file(?ID_HOWTO)); -get_external_help_doc(HelpId) -> - filename:join("http://www.erlang.org/doc/apps/observer",help_file(HelpId)). - -observer_doc_dir() -> - filename:join([code:lib_dir(observer),"doc","html"]). - -erts_doc_dir() -> - ErtsVsn = erlang:system_info(version), - RootDir = code:root_dir(), - VsnErtsDir = filename:join(RootDir,"erts-"++ErtsVsn), - DocDir = filename:join(["doc","html"]), - case filelib:is_dir(VsnErtsDir) of - true -> - filename:join(VsnErtsDir,DocDir); - false -> - %% So this can be run in source tree - filename:join([RootDir,"erts",DocDir]) - end. - -help_file(?wxID_HELP) -> "crashdump_help.html"; -help_file(?ID_UG) -> "crashdump_ug.html"; -help_file(?ID_HOWTO) -> "crash_dump.html". diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src index e881cb3c97..ef979681ac 100644 --- a/lib/observer/src/observer.app.src +++ b/lib/observer/src/observer.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2012. All Rights Reserved. +%% Copyright Ericsson AB 2002-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 @@ -20,13 +20,33 @@ [{description, "OBSERVER version 1"}, {vsn, "%VSN%"}, {modules, [crashdump_viewer, - crashdump_viewer_html, + cdv_atom_cb, + cdv_bin_cb, + cdv_detail_wx, + cdv_dist_cb, + cdv_ets_cb, + cdv_fun_cb, + cdv_gen_cb, + cdv_html_wx, + cdv_info_wx, + cdv_int_tab_cb, + cdv_mem_cb, + cdv_mod_cb, + cdv_multi_wx, + cdv_port_cb, + cdv_proc_cb, + cdv_table_wx, + cdv_term_cb, + cdv_timer_cb, + cdv_virtual_list_wx, + cdv_wx, etop, etop_gui, etop_tr, etop_txt, observer, observer_app_wx, + observer_html_lib, observer_lib, observer_perf_wx, observer_pro_wx, diff --git a/lib/observer/src/observer_html_lib.erl b/lib/observer/src/observer_html_lib.erl new file mode 100644 index 0000000000..9f77891426 --- /dev/null +++ b/lib/observer/src/observer_html_lib.erl @@ -0,0 +1,388 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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_html_lib). + +%% +%% This module implements the HTML generation for the crashdump +%% viewer. No logic or states are kept by this module. +%% + +-export([plain_page/1, + expandable_term/3, + warning/1]). + +-include("crashdump_viewer.hrl"). +-include("observer_defs.hrl"). + +%%%----------------------------------------------------------------- +%%% Display the given information as is, no heading +%%% Empty body if no info exists. +warning(Info) -> + header(body(warning_body(Info))). + +warning_body(Info) -> + [warn(Info)]. + +%%%----------------------------------------------------------------- +%%% Display the given information as is, no heading +%%% Empty body if no info exists. +plain_page(Info) -> + header(body(plain_body(Info))). + +plain_body(Info) -> + [pre(href_proc_port(lists:flatten(Info)))]. + +%%%----------------------------------------------------------------- +%%% Expanded memory +expandable_term(Heading,Expanded,Tab) -> + header(Heading,body(expandable_term_body(Heading,Expanded,Tab))). + +expandable_term_body(Heading,[],_Tab) -> + [case Heading of + "MsgQueue" -> "No messages were found"; + "Message Queue" -> "No messages were found"; + "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." + end]; +expandable_term_body(Heading,Expanded,Tab) -> + Attr = "BORDER=0 CELLPADDING=0 CELLSPACING=1 WIDTH=100%", + [case Heading of + "MsgQueue" -> + table(Attr, + [tr( + [th("WIDTH=70%","Message"), + th("WIDTH=30%","SeqTraceToken")]) | + element(1, lists:mapfoldl(fun(Msg, Even) -> + {msgq_table(Tab, Msg, Even), + not Even} + end, + true, Expanded))]); + "Message Queue" -> + table(Attr, + [tr( + [th("WIDTH=10%","Id"), + th("WIDTH=90%","Message")]) | + element(1, lists:mapfoldl(fun(Msg, {Even,N}) -> + {msgq_table(Tab, Msg, N, Even), + {not Even, N+1}} + end, + {true,1}, Expanded))]); + "StackDump" -> + table(Attr, + [tr( + [th("WIDTH=20%","Label"), + th("WIDTH=80%","Term")]) | + element(1, lists:mapfoldl(fun(Entry, Even) -> + {stackdump_table(Tab, Entry, Even), + not Even} + end, true, Expanded))]); + "ProcState" -> + table(Attr, + [tr( + [th("WIDTH=20%","Label"), + th("WIDTH=80%","Information")]) | + element(1, lists:mapfoldl(fun(Entry, Even) -> + {proc_state(Tab, Entry,Even), + not Even} + end, true, Expanded))]); + _ -> + table(Attr, + [tr( + [th("WIDTH=30%","Key"), + th("WIDTH=70%","Value")]) | + element(1, lists:mapfoldl(fun(Entry, Even) -> + {dict_table(Tab, Entry,Even), + not Even} + end, true, Expanded))]) + end]. + +msgq_table(Tab,{Msg0,Token0}, Even) -> + Token = case Token0 of + [] -> ""; + _ -> io_lib:fwrite("~w",[Token0]) + end, + Msg = all_or_expand(Tab,Msg0), + tr(color(Even),[td(pre(Msg)), td(Token)]). + +msgq_table(Tab,Msg0, Id, Even) -> + Msg = all_or_expand(Tab,Msg0), + tr(color(Even),[td(integer_to_list(Id)), td(pre(Msg))]). + +stackdump_table(Tab,{Label0,Term0},Even) -> + Label = io_lib:format("~w",[Label0]), + Term = all_or_expand(Tab,Term0), + tr(color(Even), [td("VALIGN=center",pre(Label)), td(pre(Term))]). + +dict_table(Tab,{Key0,Value0}, Even) -> + Key = all_or_expand(Tab,Key0), + Value = all_or_expand(Tab,Value0), + tr(color(Even), [td("VALIGN=center",pre(Key)), td(pre(Value))]). + +proc_state(Tab,{Key0,Value0}, Even) -> + Key = lists:flatten(io_lib:format("~s",[Key0])), + Value = all_or_expand(Tab,Value0), + tr(color(Even), [td("VALIGN=center",Key), td(pre(Value))]). + +all_or_expand(Tab,Term) -> + Preview = io_lib:format("~P",[Term,8]), + Check = io_lib:format("~P",[Term,100]), + Exp = Preview=/=Check, + all_or_expand(Tab,Term,Preview,Exp). +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(), + ets:insert(Tab,{Key,Term}), + [href_proc_port(lists:flatten(Preview), false), $\n, + href("TARGET=\"expanded\"", + ["#Term?key1="++integer_to_list(Key1)++ + "&key2="++integer_to_list(Key2)++ + "&key3="++integer_to_list(Key3)], + "Click to expand above term")]; +all_or_expand(Tab,Bin,PreviewStr,true) when is_binary(Bin) -> + <> = Bin, + Size = byte_size(Bin), + Hash = erlang:phash2(Bin), + Key = {Preview, Size, Hash}, + ets:insert(Tab,{Key,Bin}), + [href_proc_port(lists:flatten(PreviewStr), false), $\n, + href("TARGET=\"expanded\"", + ["#OBSBinary?key1="++integer_to_list(Preview)++ + "&key2="++integer_to_list(Size)++ + "&key3="++integer_to_list(Hash)], + "Click to expand above term")]. + + + +color(true) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_EVEN)); +color(false) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_ODD)). + +%%%----------------------------------------------------------------- +%%% Internal library +start_html() -> + "\n". +stop_html() -> + "". +start_html_body() -> + "\n". +stop_html_body() -> + "\n". + +header(Body) -> + header("","",Body). +header(Title,Body) -> + header(Title,"",Body). +header(Title,JavaScript,Body) -> + [%only_http_header(), + html_header(Title,JavaScript,Body)]. + +html_header(Title,JavaScript,Body) -> + [start_html(), + only_html_header(Title,JavaScript), + Body, + stop_html()]. + +only_html_header(Title,JavaScript) -> + ["\n", + "", Title, "\n", + JavaScript, + "\n"]. + +body(Text) -> + [start_html_body(), + Text, + stop_html_body()]. + +start_table(Args) -> + ["\n"]. +stop_table() -> + "
\n". + +table(Args,Text) -> + [start_table(Args), Text, stop_table()]. +tr(Text) -> + ["\n", Text, "\n\n"]. +tr(Args,Text) -> + ["\n", Text, "\n\n"]. +th(Args,Text) -> + ["\n", Text, "\n\n"]. +td(Text) -> + ["", Text, ""]. +td(Args,Text) -> + ["", Text, ""]. + +start_pre() -> + "
".
+stop_pre() ->
+    "
". +pre(Text) -> + [start_pre(),Text,stop_pre()]. +href(Link,Text) -> + ["",Text,""]. +href(Args,Link,Text) -> + ["",Text,""]. +font(Args,Text) -> + ["\n",Text,"\n\n"]. +p(Text) -> + ["

",Text,"

\n"]. +br() -> + "
\n". + + +%% In all the following, "<" is changed to "<" and ">" is changed to ">" +href_proc_port(Text) -> + href_proc_port(Text,true). +href_proc_port(Text,LinkToBin) -> + href_proc_port(Text,[],LinkToBin). +href_proc_port("#Ref<"++T,Acc,LTB) -> + %% No links to refs + href_proc_port(T,["#Ref<"|Acc],LTB); +href_proc_port("#Fun<"++T,Acc,LTB) -> + %% No links to funs + href_proc_port(T,["#Fun<"|Acc],LTB); +href_proc_port("#Port<"++T,Acc,LTB) -> + {Port0,Rest} = split($>,T), + Port = "#Port<"++Port0 ++ ">", + href_proc_port(Rest,[href(Port,Port)|Acc],LTB); +href_proc_port("<<"++T,Acc,LTB) -> + %% No links to binaries + href_proc_port(T,["<<"|Acc],LTB); +href_proc_port("<"++([C|_]=T),Acc,LTB) when $0 =< C, C =< $9 -> + %% Pid + {Pid0,Rest} = split($>,T), + Pid = "<" ++ Pid0 ++ ">", + href_proc_port(Rest,[href(Pid,Pid)|Acc],LTB); +href_proc_port("['#CDVBin'"++T,Acc,LTB) -> + %% Binary written by crashdump_viewer:parse_heap_term(...) + href_proc_bin(cdv, T, Acc, LTB); +href_proc_port("['#OBSBin'"++T,Acc,LTB) -> + %% Binary written by crashdump_viewer:parse_heap_term(...) + href_proc_bin(obs, T, Acc, LTB); +href_proc_port("['#CDVPort'"++T,Acc,LTB) -> + %% Port written by crashdump_viewer:parse_term(...) + {Port0,Rest} = split($],T), + PortStr= + case string:tokens(Port0,",.|") of + [X,Y] -> + Port = "#Port<"++X++"."++Y++">", + href(Port,Port); + Ns -> + "#Port<" ++ string:join(Ns,".") ++"...>" + end, + href_proc_port(Rest,[PortStr|Acc],LTB); +href_proc_port("['#CDVPid'"++T,Acc,LTB) -> + %% Pid written by crashdump_viewer:parse_term(...) + {Pid0,Rest} = split($],T), + PidStr = + case string:tokens(Pid0,",.|") of + [X,Y,Z] -> + Pid = "<"++X++"."++Y++"."++Z++">", + href(Pid,Pid); + Ns -> + "<" ++ string:join(Ns,".") ++ "...>" + end, + href_proc_port(Rest,[PidStr|Acc],LTB); +href_proc_port("'#CDVIncompleteHeap'"++T,Acc,LTB)-> + %% The heap is incomplete! Written by crashdump_viewer:deref_pts(...) + IH = lists:reverse( + lists:flatten( + "...(Incomplete Heap)")), + href_proc_port(T,IH++Acc,LTB); +href_proc_port("'#CDVTruncatedBinary'"++T,Acc,LTB)-> + %% A binary which is truncated! Written by + %% crashdump_viewer:parse_heap_term(...) + IH = lists:reverse( + lists:flatten( + "<<...(Truncated Binary)>>" + "")), + href_proc_port(T,IH++Acc,LTB); +href_proc_port("'#CDVNonexistingBinary'"++T,Acc,LTB)-> + %% A binary which could not be found in the dump! Written by + %% crashdump_viewer:parse_heap_term(...) + IH = lists:reverse( + lists:flatten( + "<<...(Nonexisting Binary)>>" + "")), + href_proc_port(T,IH++Acc,LTB); +href_proc_port("<"++T,Acc,LTB) -> + href_proc_port(T,["<"|Acc],LTB); +href_proc_port(">"++T,Acc,LTB) -> + href_proc_port(T,[">"|Acc],LTB); +href_proc_port([H|T],Acc,LTB) -> + href_proc_port(T,[H|Acc],LTB); +href_proc_port([],Acc,_) -> + lists:reverse(Acc). + +href_proc_bin(From, T, Acc, LTB) -> + {OffsetSizePos,Rest} = split($],T), + BinStr = + case string:tokens(OffsetSizePos,",.| \n") of + [Offset,Size,Pos] when From =:= cdv -> + Id = {list_to_integer(Offset),10,list_to_integer(Pos)}, + {ok,PreviewBin} = crashdump_viewer:expand_binary(Id), + PreviewStr = ["<<", + [integer_to_list(X)++"," || <> <= PreviewBin], + "...(", + observer_lib:to_str({bytes,Size}), + ")>>"], + if LTB -> + href("TARGET=\"expanded\"", + ["#Binary?offset="++Offset++ + "&size="++Size++ + "&pos="++Pos], + PreviewStr); + true -> + PreviewStr + end; + [Preview,Size,Md5] when From =:= obs -> + PreviewBin = <<(list_to_integer(Preview)):80>>, + PreviewStr = ["<<", + [integer_to_list(X)++"," || <> <= PreviewBin], + "...(", + observer_lib:to_str({bytes,list_to_integer(Size)}), + ")>>"], + if LTB -> + href("TARGET=\"expanded\"", + ["#OBSBinary?key1="++Preview++ + "&key2="++Size++ + "&key3="++Md5], + PreviewStr); + true -> + PreviewStr + end; + _ -> + "<< ... >>" + end, + href_proc_port(Rest,[BinStr|Acc],LTB). + +split(Char,Str) -> + split(Char,Str,[]). +split(Char,[Char|Str],Acc) -> % match Char + {lists:reverse(Acc),Str}; +split(Char,[H|T],Acc) -> + split(Char,T,[H|Acc]). + + +warn([]) -> + []; +warn(Warning) -> + font("COLOR=\"#FF0000\"",p([Warning,br(),br()])). diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl index af2b8dda2e..988e04993c 100644 --- a/lib/observer/src/observer_procinfo.erl +++ b/lib/observer/src/observer_procinfo.erl @@ -126,8 +126,8 @@ handle_event(#wx{obj=Obj, event=#wxMouse{type=leave_window}}, State) -> handle_event(#wx{event=#wxHtmlLink{linkInfo=#wxHtmlLinkInfo{href=Href}}}, #state{frame=Frame,expand_table=T,expand_wins=Opened0}=State) -> {Type, Rest} = case Href of - "#Term?"++Keys -> {cdv_term_wx, Keys}; - "#OBSBinary?"++Keys -> {cdv_bin_wx, Keys}; + "#Term?"++Keys -> {cdv_term_cb, Keys}; + "#OBSBinary?"++Keys -> {cdv_bin_cb, Keys}; _ -> {other, Href} end, case Type of @@ -142,7 +142,7 @@ handle_event(#wx{event=#wxHtmlLink{linkInfo=#wxHtmlLinkInfo{href=Href}}}, Opened = case lists:keyfind(Id,1,Opened0) of false -> - Win = cdv_detail_win:start_link(Id,Frame,Callback), + Win = cdv_detail_wx:start_link(Id,Frame,Callback), [{Id,Win}|Opened0]; {_,Win} -> wxFrame:raise(Win), @@ -201,7 +201,7 @@ init_message_page(Parent, Pid, Table) -> [Pid, messages]) of {messages, Messages} -> - Html = crashdump_viewer_html:expandable_term("Message Queue", Messages, Table), + Html = observer_html_lib:expandable_term("Message Queue", Messages, Table), wxHtmlWindow:setPage(Win, Html); _ -> throw(process_undefined) @@ -216,7 +216,7 @@ init_dict_page(Parent, Pid, Table) -> case observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, dictionary]) of {dictionary,Dict} -> - Html = crashdump_viewer_html:expandable_term("Dictionary", Dict, Table), + Html = observer_html_lib:expandable_term("Dictionary", Dict, Table), wxHtmlWindow:setPage(Win, Html); _ -> throw(process_undefined) @@ -273,7 +273,7 @@ init_state_page(Parent, Pid, Table) -> Win = observer_lib:html_window(Parent), Update = fun() -> StateInfo = fetch_state_info(Pid), - Html = crashdump_viewer_html:expandable_term("ProcState", StateInfo, Table), + Html = observer_html_lib:expandable_term("ProcState", StateInfo, Table), wxHtmlWindow:setPage(Win, Html) end, Update(), -- cgit v1.2.3