diff options
Diffstat (limited to 'lib/observer')
-rw-r--r-- | lib/observer/doc/src/Makefile | 18 | ||||
-rw-r--r-- | lib/observer/src/Makefile | 28 | ||||
-rw-r--r-- | lib/observer/src/crashdump_viewer_html.erl | 4 | ||||
-rw-r--r-- | lib/observer/src/observer.app.src | 12 | ||||
-rw-r--r-- | lib/observer/src/observer_app_wx.erl | 36 | ||||
-rw-r--r-- | lib/observer/src/observer_lib.erl | 54 | ||||
-rw-r--r-- | lib/observer/src/observer_perf_wx.erl | 2 | ||||
-rw-r--r-- | lib/observer/src/observer_pro_wx.erl | 24 | ||||
-rw-r--r-- | lib/observer/src/observer_procinfo.erl | 36 | ||||
-rw-r--r-- | lib/observer/src/observer_sys_wx.erl | 4 | ||||
-rw-r--r-- | lib/observer/src/observer_trace_wx.erl | 4 | ||||
-rw-r--r-- | lib/observer/src/observer_traceoptions_wx.erl | 8 | ||||
-rw-r--r-- | lib/observer/src/observer_tv_table.erl | 66 | ||||
-rw-r--r-- | lib/observer/src/observer_wx.erl | 57 | ||||
-rw-r--r-- | lib/observer/test/Makefile | 6 | ||||
-rw-r--r-- | lib/observer/test/crashdump_viewer_SUITE.erl | 46 |
16 files changed, 294 insertions, 111 deletions
diff --git a/lib/observer/doc/src/Makefile b/lib/observer/doc/src/Makefile index cd9f9466ca..0f564d3299 100644 --- a/lib/observer/doc/src/Makefile +++ b/lib/observer/doc/src/Makefile @@ -133,16 +133,16 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile index 91a4c656ad..7135a6abd5 100644 --- a/lib/observer/src/Makefile +++ b/lib/observer/src/Makefile @@ -129,20 +129,20 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_SCRIPT) $(EXECUTABLES) $(RELSYSDIR)/priv/bin - $(INSTALL_DIR) $(RELSYSDIR)/priv/crashdump_viewer - $(INSTALL_DATA) $(WEBTOOLFILES) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(GIF_FILES) $(RELSYSDIR)/priv/crashdump_viewer + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(EXAMPLE_FILES) "$(RELSYSDIR)/examples" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_SCRIPT) $(EXECUTABLES) "$(RELSYSDIR)/priv/bin" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/crashdump_viewer" + $(INSTALL_DATA) $(WEBTOOLFILES) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(GIF_FILES) "$(RELSYSDIR)/priv/crashdump_viewer" release_docs_spec: diff --git a/lib/observer/src/crashdump_viewer_html.erl b/lib/observer/src/crashdump_viewer_html.erl index 24a80b1916..3151b83bfb 100644 --- a/lib/observer/src/crashdump_viewer_html.erl +++ b/lib/observer/src/crashdump_viewer_html.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 @@ -1394,7 +1394,7 @@ timers_table(Timer) -> td("ALIGN=right",Time)]). loaded_mods_table(#loaded_mod{mod=Mod,current_size=CS,old_size=OS}) -> - tr([td(href(["loaded_mod_details?mod=",Mod],Mod)), + tr([td(href(["loaded_mod_details?mod=",http_uri:encode(Mod)],Mod)), td("ALIGN=right",CS), td("ALIGN=right",OS)]). diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src index 5c65ea5c8f..d3aaf351dd 100644 --- a/lib/observer/src/observer.app.src +++ b/lib/observer/src/observer.app.src @@ -25,6 +25,18 @@ etop_gui, etop_tr, etop_txt, + observer, + observer_app_wx, + observer_lib, + observer_perf_wx, + observer_pro_wx, + observer_procinfo, + observer_sys_wx, + observer_trace_wx, + observer_traceoptions_wx, + observer_tv_table, + observer_tv_wx, + observer_wx, ttb, ttb_et]}, {registered, []}, diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index 7eac2b8fab..380532e90c 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -147,11 +147,11 @@ setup_scrollbar({CW, CH}, AppWin, #app{dim={W0,H0}}) -> H = max(H0,CH), PPC = 20, if W0 =< CW, H0 =< CH -> - wxScrolledWindow:setScrollbars(AppWin, W, H, 1, 1); + wxScrolledWindow:setScrollbars(AppWin, W, H, 0, 0); H0 =< CH -> - wxScrolledWindow:setScrollbars(AppWin, PPC, H, W div PPC+1, 1); + wxScrolledWindow:setScrollbars(AppWin, PPC, H, W div PPC+1, 0); W0 =< CW -> - wxScrolledWindow:setScrollbars(AppWin, W, PPC, 1, H div PPC+1); + wxScrolledWindow:setScrollbars(AppWin, W, PPC, 0, H div PPC+1); true -> wxScrolledWindow:setScrollbars(AppWin, PPC, PPC, W div PPC+1, H div PPC+1) end; @@ -204,7 +204,7 @@ handle_event(#wx{id=?ID_PROC_MSG, event=#wxCommand{type=command_menu_selected}}, handle_event(#wx{id=?ID_PROC_KILL, event=#wxCommand{type=command_menu_selected}}, State = #state{panel=Panel, sel={#box{s1=#str{pid=Pid}},_}}) -> - case observer_lib:user_term(Panel, "Enter Exit Reason", "") of + case observer_lib:user_term(Panel, "Enter Exit Reason", "kill") of cancel -> ok; {ok, Term} -> exit(Pid, Term); {error, Error} -> observer_lib:display_info_dialog(Error) @@ -269,22 +269,21 @@ handle_cast(Event, _State) -> %%%%%%%%%% handle_info({active, Node}, State = #state{parent=Parent, current=Curr, appmon=Appmon}) -> create_menus(Parent, []), - {ok, Pid} = appmon_info:start_link(Node, self(), []), - case Appmon of - undefined -> ok; - Pid -> ok; - _ -> %% Deregister me as client (and stop appmon if last) - exit(Appmon, normal) - end, + Pid = try + Node = node(Appmon), + Appmon + catch _:_ -> + {ok, P} = appmon_info:start_link(Node, self(), []), + P + end, appmon_info:app_ctrl(Pid, Node, true, []), (Curr =/= undefined) andalso appmon_info:app(Pid, Curr, true, []), {noreply, State#state{appmon=Pid}}; - -handle_info(not_active, State = #state{appmon=AppMon, current=Prev}) -> +handle_info(not_active, State = #state{appmon=AppMon}) -> appmon_info:app_ctrl(AppMon, node(AppMon), false, []), - (Prev =/= undefined) andalso appmon_info:app(AppMon, Prev, false, []), - {noreply, State}; - + lists:member(node(AppMon), nodes()) andalso exit(AppMon, normal), + observer_wx:set_status(""), + {noreply, State#state{appmon=undefined}}; handle_info({delivery, Pid, app_ctrl, _, Apps0}, State = #state{appmon=Pid, apps_w=LBox, current=Curr0}) -> Apps = [atom_to_list(App) || {_, App, {_, _, _}} <- Apps0], @@ -341,6 +340,7 @@ handle_mouse_click(Node = {#box{s1=#str{pid=Pid}},_}, Type, right_down -> popup_menu(Panel); _ -> ok end, + observer_wx:set_status(io_lib:format("Pid: ~p", [Pid])), wxWindow:refresh(AppWin), State#state{sel=Node}; handle_mouse_click(_, _, State = #state{sel=undefined}) -> @@ -349,6 +349,7 @@ handle_mouse_click(_, right_down, State=#state{panel=Panel}) -> popup_menu(Panel), State; handle_mouse_click(_, _, State=#state{app_w=AppWin}) -> + observer_wx:set_status(""), wxWindow:refresh(AppWin), State#state{sel=undefined}. @@ -376,10 +377,11 @@ popup_menu(Panel) -> wxMenu:append(Menu, ?ID_TRACE_NAME, "Trace named process"), wxMenu:append(Menu, ?ID_TRACE_TREE_PIDS, "Trace process tree"), wxMenu:append(Menu, ?ID_TRACE_TREE_NAMES, "Trace named process tree"), + wxMenu:append(Menu, ?ID_PROC_MSG, "Send Msg"), + wxMenu:append(Menu, ?ID_PROC_KILL, "Kill process"), wxWindow:popupMenu(Panel, Menu), wxMenu:destroy(Menu). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% locate_node(X, _Y, [{Box=#box{x=BX}, _Chs}|_Rest]) when X < BX -> diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl index 3b924d46cf..4077f8371a 100644 --- a/lib/observer/src/observer_lib.erl +++ b/lib/observer/src/observer_lib.erl @@ -19,7 +19,7 @@ -module(observer_lib). -export([get_wx_parent/1, - display_info_dialog/1, user_term/3, + display_info_dialog/1, user_term/3, user_term_multiline/3, interval_dialog/4, start_timer/1, stop_timer/1, display_info/2, fill_info/2, update_info/2, to_str/1, create_menus/3, create_menu_item/3, @@ -347,6 +347,58 @@ user_term(Parent, Title, Default) -> cancel end. +user_term_multiline(Parent, Title, Default) -> + Dialog = wxDialog:new(Parent, ?wxID_ANY, Title, + [{style, ?wxDEFAULT_DIALOG_STYLE bor + ?wxRESIZE_BORDER}]), + Panel = wxPanel:new(Dialog), + + TextCtrl = wxTextCtrl:new(Panel, ?wxID_ANY, + [{value, Default}, + {style, ?wxDEFAULT bor ?wxTE_MULTILINE}]), + Line = wxStaticLine:new(Panel, [{style, ?wxLI_HORIZONTAL}]), + + Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL), + + InnerSizer = wxBoxSizer:new(?wxVERTICAL), + wxSizer:add(InnerSizer, TextCtrl, + [{flag, ?wxEXPAND bor ?wxALL},{proportion, 1},{border, 5}]), + wxSizer:add(InnerSizer, Line, + [{flag, ?wxEXPAND},{proportion, 0},{border, 5}]), + wxPanel:setSizer(Panel, InnerSizer), + + TopSizer = wxBoxSizer:new(?wxVERTICAL), + wxSizer:add(TopSizer, Panel, + [{flag, ?wxEXPAND bor ?wxALL},{proportion, 1},{border, 5}]), + wxSizer:add(TopSizer, Buttons, + [{flag, ?wxEXPAND bor ?wxBOTTOM bor ?wxRIGHT},{border, 10}]), + + % calculate the size of TopSizer when the whole user_term + % fits in the TextCtrl + DC = wxClientDC:new(Panel), + W = wxDC:getCharWidth(DC), + H = wxDC:getCharHeight(DC), + {EW, EH} = wxDC:getMultiLineTextExtent(DC, Default), + wxSizer:setItemMinSize(InnerSizer, 0, EW+2*W, EH+H), + TopSize = wxSizer:getMinSize(TopSizer), + % reset min size of TextCtrl to 40 chararacters * 4 lines + wxSizer:setItemMinSize(InnerSizer, 0, 40*W, 4*H), + + wxWindow:setSizerAndFit(Dialog, TopSizer), + wxSizer:setSizeHints(TopSizer, Dialog), + + wxWindow:setClientSize(Dialog, TopSize), + + case wxDialog:showModal(Dialog) of + ?wxID_OK -> + Str = wxTextCtrl:getValue(TextCtrl), + wxDialog:destroy(Dialog), + parse_string(ensure_last_is_dot(Str)); + ?wxID_CANCEL -> + wxDialog:destroy(Dialog), + cancel + end. + parse_string(Str) -> try Tokens = case erl_scan:string(Str) of diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index fa867e12f6..abf90ac612 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -123,7 +123,7 @@ init([Notebook, Parent]) -> }} catch _:Err -> io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]), - {error, Err} + {stop, Err} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index 7578215ff9..ee67664539 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. 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 @@ -191,13 +191,20 @@ dump_to_file(Parent, FileName, Holder) -> start_procinfo(undefined, _Frame, Opened) -> Opened; start_procinfo(Pid, Frame, Opened) -> - case lists:member(Pid, Opened) of - true -> - Opened; - false -> - observer_procinfo:start(Pid, Frame, self()), - [Pid | Opened] + %% This code doesn't work until we collect which windows have been + %% closed maybe it should moved to observer_wx.erl + %% and add a global menu which remembers windows. + %% case lists:keyfind(Pid, 1, Opened) of + %% false -> + case observer_procinfo:start(Pid, Frame, self()) of + {error, _} -> Opened; + PI -> [{Pid, PI} | Opened] end. + %%; + %% {_, PI} -> + %% wxFrame:raise(PI), + %% Opened + %% end. call(Holder, What) -> Ref = erlang:monitor(process, Holder), @@ -251,8 +258,7 @@ terminate(_Reason, #state{holder=Holder}) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. - + {ok, State}. handle_call(Msg, _From, State) -> io:format("~p:~p: Unhandled call ~p~n",[?MODULE, ?LINE, Msg]), diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl index ec08d3aff1..45218c177b 100644 --- a/lib/observer/src/observer_procinfo.erl +++ b/lib/observer/src/observer_procinfo.erl @@ -49,7 +49,8 @@ init([Pid, ParentFrame, Parent]) -> try Title=case observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, registered_name]) of [] -> io_lib:format("~p",[Pid]); - {registered_name, Registered} -> atom_to_list(Registered) + {registered_name, Registered} -> io_lib:format("~p (~p)",[Registered, Pid]); + undefined -> throw(process_undefined) end, Frame=wxFrame:new(ParentFrame, ?wxID_ANY, [atom_to_list(node(Pid)), $:, Title], [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {850,600}}]), @@ -75,7 +76,10 @@ init([Pid, ParentFrame, Parent]) -> }} catch error:{badrpc, _} -> observer_wx:return_to_localnode(ParentFrame, node(Pid)), - {stop, badrpc, #state{parent=Parent, pid=Pid}} + {stop, badrpc}; + process_undefined -> + observer_lib:display_info_dialog("No such alive process"), + {stop, normal} end. init_panel(Notebook, Str, Pid, Fun) -> @@ -94,8 +98,11 @@ handle_event(#wx{event=#wxClose{type=close_window}}, State) -> handle_event(#wx{id=?wxID_CLOSE, event=#wxCommand{type=command_menu_selected}}, State) -> {stop, normal, State}; -handle_event(#wx{id=?REFRESH}, #state{pages=Pages}=State) -> - [(W#worker.callback)() || W <- Pages], +handle_event(#wx{id=?REFRESH}, #state{frame=Frame, pid=Pid, pages=Pages}=State) -> + try [(W#worker.callback)() || W <- Pages] + catch process_undefined -> + wxFrame:setTitle(Frame, io_lib:format("*DEAD* ~p",[Pid])) + end, {noreply, State}; handle_event(Event, _State) -> @@ -120,17 +127,15 @@ terminate(_Reason, #state{parent=Parent,pid=Pid,frame=Frame}) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. + {ok, State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_process_page(Panel, Pid) -> Fields0 = process_info_fields(Pid), {FPanel, _, UpFields} = observer_lib:display_info(Panel, Fields0), - {FPanel, fun() -> case process_info_fields(Pid) of - Fields when is_list(Fields) -> - observer_lib:update_info(UpFields, Fields); - _ -> ok - end + {FPanel, fun() -> + Fields = process_info_fields(Pid), + observer_lib:update_info(UpFields, Fields) end}. init_text_page(Parent) -> @@ -162,7 +167,8 @@ init_message_page(Parent, Pid) -> false -> wxTextCtrl:writeText(Text, Messages) end; - _ -> ok + _ -> + throw(process_undefined) end end, Update(), @@ -178,7 +184,8 @@ init_dict_page(Parent, Pid) -> Last = wxTextCtrl:getLastPosition(Text), wxTextCtrl:remove(Text, 0, Last), wxTextCtrl:writeText(Text, Dict); - _ -> ok + _ -> + throw(process_undefined) end end, Update(), @@ -216,7 +223,8 @@ init_stack_page(Parent, Pid) -> wxListCtrl:setItem(LCtrl, Row, 1, FileLine), Row+1 end, 0, RawBt); - _ -> ok + _ -> + throw(process_undefined) end end, Resize = fun(#wx{event=#wxSize{size={W,_}}},Ev) -> @@ -266,7 +274,7 @@ process_info_fields(Pid) -> RawInfo when is_list(RawInfo) -> observer_lib:fill_info(Struct, RawInfo); _ -> - ok + throw(process_undefined) end. item_list() -> diff --git a/lib/observer/src/observer_sys_wx.erl b/lib/observer/src/observer_sys_wx.erl index 09602bbd9e..f00a666a35 100644 --- a/lib/observer/src/observer_sys_wx.erl +++ b/lib/observer/src/observer_sys_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. 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 @@ -147,7 +147,7 @@ terminate(_Reason, _State) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. + {ok, State}. handle_call(Msg, _From, State) -> io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]), diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index d0b6a1e063..f2a1084f85 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. 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 @@ -489,7 +489,7 @@ terminate(_Reason, #state{nodes=_Nodes}) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. + {ok, State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_add_patterns({Module, NewPs}, State=#state{tpatterns=TPs0, m_view=Mview, f_view=Fview}) -> diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl index 6a634e06f0..e27f565abc 100644 --- a/lib/observer/src/observer_traceoptions_wx.erl +++ b/lib/observer/src/observer_traceoptions_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. 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 @@ -167,8 +167,10 @@ module_selector(Parent, Node) -> function_selector(Parent, Node, Module) -> Functions = observer_wx:try_rpc(Node, Module, module_info, [functions]), - Choices = lists:sort([{Name, Arity} || {Name, Arity} <- Functions, - not(erl_internal:guard_bif(Name, Arity))]), + Externals = observer_wx:try_rpc(Node, Module, module_info, [exports]), + + Choices = lists:usort([{Name, Arity} || {Name, Arity} <- Externals ++ Functions, + not(erl_internal:guard_bif(Name, Arity))]), ParsedChoices = parse_function_names(Choices), case check_selector(Parent, ParsedChoices) of [] -> [{Module, '_', '_'}]; diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl index f432173f57..c41f0f006a 100644 --- a/lib/observer/src/observer_tv_table.erl +++ b/lib/observer/src/observer_tv_table.erl @@ -24,6 +24,8 @@ -export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, handle_event/2, handle_sync_event/3, handle_cast/2]). +-export([format/1]). + -include("observer_defs.hrl"). -import(observer_lib, [to_str/1]). @@ -218,8 +220,8 @@ search_area(Parent) -> search=TC1,goto=TC2,radio={Nbtn,Pbtn,Cbtn}}. edit(Index, #state{pid=Pid, frame=Frame}) -> - Str = get_row(Pid, Index, all), - case observer_lib:user_term(Frame, "Edit object:", Str) of + Str = get_row(Pid, Index, all_multiline), + case observer_lib:user_term_multiline(Frame, "Edit object:", Str) of cancel -> ok; {ok, Term} -> Pid ! {edit, Index, Term}; Err = {error, _} -> self() ! Err @@ -265,7 +267,8 @@ handle_event(#wx{id=?ID_DELETE}, wxStatusBar:setStatusText(StatusBar, io_lib:format("Deleted object: ~s",[Str])), {noreply, State}; -handle_event(#wx{id=?wxID_CLOSE}, State) -> +handle_event(#wx{id=?wxID_CLOSE}, State = #state{frame=Frame}) -> + wxFrame:destroy(Frame), {stop, normal, State}; handle_event(Help = #wx{id=?wxID_HELP}, State) -> @@ -321,7 +324,7 @@ handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{type=command_text_enter,cmdS wxStatusBar:setStatusText(SB, "Not found"), Pid ! {mark_search_hit, Find#find.start}, wxListCtrl:refreshItem(Grid, Find#find.start), - {noreply, State#state{search=Search#search{find=#find{found=false}}}}; + {noreply, State#state{search=Search#search{find=Find#find{found=false}}}}; Row -> wxListCtrl:ensureVisible(Grid, Row), wxListCtrl:refreshItem(Grid, Row), @@ -453,7 +456,7 @@ get_attr(Table, Item) -> Ref = erlang:monitor(process, Table), Table ! {get_attr, self(), Item}, receive - {'DOWN', Ref, _, _, _} -> ""; + {'DOWN', Ref, _, _, _} -> wx:null(); {Table, Res} -> erlang:demonitor(Ref), Res @@ -594,7 +597,7 @@ keysort(Col, Table) -> lists:sort(Sort, Table). search([Str, Row, Dir0, CaseSens], - S=#holder{parent=Parent, table=Table}) -> + S=#holder{parent=Parent, table=Table0}) -> Opt = case CaseSens of true -> []; false -> [caseless] @@ -605,29 +608,35 @@ search([Str, Row, Dir0, CaseSens], end, Res = case re:compile(Str, Opt) of {ok, Re} -> + Table = + case Dir0 of + true -> + lists:nthtail(Row, Table0); + false -> + lists:reverse(lists:sublist(Table0, Row+1)) + end, search(Row, Dir, Re, Table); {error, _} -> false end, Parent ! {self(), Res}, S#holder{search=Res}. -search(Row, Dir, Re, Table) -> - Res = try lists:nth(Row+1, Table) of - Term -> - Str = format(Term), - re:run(Str, Re) - catch _:_ -> no_more - end, +search(Row, Dir, Re, [ [Term|_] |Table]) -> + Str = format(Term), + Res = re:run(Str, Re), case Res of nomatch -> search(Row+Dir, Dir, Re, Table); - no_more -> false; {match,_} -> Row - end. + end; +search(_, _, _, []) -> + false. get_row(From, Row, Col, Table) -> case lists:nth(Row+1, Table) of [Object|_] when Col =:= all -> From ! {self(), format(Object)}; + [Object|_] when Col =:= all_multiline -> + From ! {self(), io_lib:format("~p", [Object])}; [Object|_] when tuple_size(Object) >= Col -> From ! {self(), format(element(Col, Object))}; _ -> @@ -747,6 +756,13 @@ format(List) when is_list(List) -> format_list(List); format(Bin) when is_binary(Bin), byte_size(Bin) > 100 -> io_lib:format("<<#Bin:~w>>", [byte_size(Bin)]); +format(Bin) when is_binary(Bin) -> + try + true = printable_list(unicode:characters_to_list(Bin)), + io_lib:format("<<\"~ts\">>", [Bin]) + catch _:_ -> + io_lib:format("~w", [Bin]) + end; format(Float) when is_float(Float) -> io_lib:format("~.3g", [Float]); format(Term) -> @@ -762,7 +778,7 @@ format_tuple(_Tuple, 1, 0) -> format_list([]) -> "[]"; format_list(List) -> case printable_list(List) of - true -> io_lib:format("\"~ts\"", [List]); + true -> io_lib:format("\"~ts\"", [map_printable_list(List)]); false -> [$[ | make_list(List)] end. @@ -771,6 +787,24 @@ make_list([Last]) -> make_list([Head|Tail]) -> [format(Head), $,|make_list(Tail)]. +map_printable_list([$\n|Cs]) -> + [$\\, $n|map_printable_list(Cs)]; +map_printable_list([$\r|Cs]) -> + [$\\, $r|map_printable_list(Cs)]; +map_printable_list([$\t|Cs]) -> + [$\\, $t|map_printable_list(Cs)]; +map_printable_list([$\v|Cs]) -> + [$\\, $v|map_printable_list(Cs)]; +map_printable_list([$\b|Cs]) -> + [$\\, $b|map_printable_list(Cs)]; +map_printable_list([$\f|Cs]) -> + [$\\, $f|map_printable_list(Cs)]; +map_printable_list([$\e|Cs]) -> + [$\\, $e|map_printable_list(Cs)]; +map_printable_list([]) -> []; +map_printable_list([C|Cs]) -> + [C|map_printable_list(Cs)]. + %% printable_list([Char]) -> bool() %% Return true if CharList is a list of printable characters, else %% false. diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index e2b256d768..e433bea8c2 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -20,7 +20,7 @@ -behaviour(wx_object). -export([start/0]). --export([create_menus/2, get_attrib/1, get_tracer/0, +-export([create_menus/2, get_attrib/1, get_tracer/0, set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]). -export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, @@ -58,7 +58,8 @@ perf_panel, active_tab, node, - nodes + nodes, + prev_node="" }). start() -> @@ -73,6 +74,9 @@ create_menus(Object, Menus) when is_list(Menus) -> get_attrib(What) -> wx_object:call(observer, {get_attrib, What}). +set_status(What) -> + wx_object:cast(observer, {status_bar, What}). + get_tracer() -> wx_object:call(observer, get_tracer). @@ -191,10 +195,13 @@ setup(#state{frame = Frame} = State) -> %%Callbacks handle_event(#wx{event=#wxNotebook{type=command_notebook_page_changing}}, #state{active_tab=Previous, node=Node} = State) -> - Pid = get_active_pid(State), - Previous ! not_active, - Pid ! {active, Node}, - {noreply, State#state{active_tab=Pid}}; + case get_active_pid(State) of + Previous -> {noreply, State}; + Pid -> + Previous ! not_active, + Pid ! {active, Node}, + {noreply, State#state{active_tab=Pid}} + end; handle_event(#wx{event = #wxClose{}}, State) -> {stop, normal, State}; @@ -258,20 +265,21 @@ handle_event(#wx{id = ?ID_CONNECT, event = #wxCommand{type = command_menu_select handle_event(#wx{id = ?ID_PING, event = #wxCommand{type = command_menu_selected}}, #state{frame = Frame} = State) -> UpdState = case create_connect_dialog(ping, State) of - cancel -> State; + cancel -> State; {value, Value} when is_list(Value) -> try Node = list_to_atom(Value), case net_adm:ping(Node) of pang -> create_txt_dialog(Frame, "Connect failed", "Pang", ?wxICON_EXCLAMATION), - State; + State#state{prev_node=Value}; pong -> - change_node_view(Node, State) + State1 = change_node_view(Node, State), + State1#state{prev_node=Value} end catch _:_ -> create_txt_dialog(Frame, "Connect failed", "Pang", ?wxICON_EXCLAMATION), - State + State#state{prev_node=Value} end end, {noreply, UpdState}; @@ -288,6 +296,10 @@ handle_event(Event, State) -> Pid ! Event, {noreply, State}. +handle_cast({status_bar, Msg}, State=#state{status_bar=SB}) -> + wxStatusBar:setStatusText(SB, Msg), + {noreply, State}; + handle_cast(_Cast, State) -> {noreply, State}. @@ -341,7 +353,7 @@ terminate(_Reason, #state{frame = Frame}) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. + {ok, State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -401,7 +413,9 @@ connect2(NodeName, Opts, Cookie) -> end. change_node_view(Node, State) -> - get_active_pid(State) ! {active, Node}, + Tab = get_active_pid(State), + Tab ! not_active, + Tab ! {active, Node}, StatusText = ["Observer - " | atom_to_list(Node)], wxFrame:setTitle(State#state.frame, StatusText), wxStatusBar:setStatusText(State#state.status_bar, StatusText), @@ -439,8 +453,8 @@ pid2panel(Pid, #state{pro_panel=Pro, sys_panel=Sys, end. -create_connect_dialog(ping, #state{frame = Frame}) -> - Dialog = wxTextEntryDialog:new(Frame, "Connect to node"), +create_connect_dialog(ping, #state{frame = Frame, prev_node=Prev}) -> + Dialog = wxTextEntryDialog:new(Frame, "Connect to node", [{value, Prev}]), case wxDialog:showModal(Dialog) of ?wxID_OK -> Value = wxTextEntryDialog:getValue(Dialog), @@ -560,7 +574,16 @@ remove_menu_items([], _MB) -> ok. get_nodes() -> - Nodes = [node()| nodes()], + Nodes0 = case erlang:is_alive() of + false -> []; + true -> + case net_adm:names() of + {error, _} -> nodes(); + {ok, Names} -> + epmd_nodes(Names) ++ nodes() + end + end, + Nodes = lists:usort(Nodes0), {_, Menues} = lists:foldl(fun(Node, {Id, Acc}) when Id < ?LAST_NODES_MENU_ID -> {Id + 1, [#create_menu{id=Id + ?FIRST_NODES_MENU_ID, @@ -568,6 +591,10 @@ get_nodes() -> end, {1, []}, Nodes), {Nodes, lists:reverse(Menues)}. +epmd_nodes(Names) -> + [_, Host] = string:tokens(atom_to_list(node()),"@"), + [list_to_atom(Name ++ [$@|Host]) || {Name, _} <- Names]. + update_node_list(State = #state{menubar=MenuBar}) -> {Nodes, NodesMenuItems} = get_nodes(), NodeMenuId = wxMenuBar:findMenu(MenuBar, "Nodes"), diff --git a/lib/observer/test/Makefile b/lib/observer/test/Makefile index bf99f07081..9df0591da5 100644 --- a/lib/observer/test/Makefile +++ b/lib/observer/test/Makefile @@ -82,11 +82,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) observer.spec $(EMAKEFILE) \ $(COVERFILE) $(ERL_FILES) \ - $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl index 5bbce9d076..6f882d0be9 100644 --- a/lib/observer/test/crashdump_viewer_SUITE.erl +++ b/lib/observer/test/crashdump_viewer_SUITE.erl @@ -414,6 +414,10 @@ special(Port,File) -> _ -> ok end; + ".strangemodname" -> + AllMods = contents(Port,"loaded_modules"), + open_all_modules(Port,AllMods), + ok; %%! No longer needed - all atoms are shown on one page!! %% ".250atoms" -> %% Html1 = contents(Port,"atoms"), @@ -496,6 +500,26 @@ expand_binary_link(Html) -> expand_binary_link(T) end. +open_all_modules(Port,Modules) -> + case get_first_module(Modules) of + {Module,Rest} -> + ModuleDetails = contents(Port,"loaded_mod_details?mod=" ++ Module), + ModTitle = http_uri:decode(Module), + ModTitle = title(ModuleDetails), + open_all_modules(Port,Rest); + false -> + ok + end. + +get_first_module([]) -> + false; +get_first_module(Html) -> + case Html of + "<TD><A HREF=\"loaded_mod_details?mod=" ++ Rest -> + {string:sub_word(Rest,1,$"),Rest}; + [_H|T] -> + get_first_module(T) + end. %% next_link(Html) -> %% case Html of @@ -565,7 +589,7 @@ create_dumps(DataDir,[Rel|Rels],Acc) -> Fun = fun() -> do_create_dumps(DataDir,Rel) end, Pa = filename:dirname(code:which(?MODULE)), {SlAllocDumps,Dumps,DosDump} = - ?t:run_on_shielded_node(Fun, compat_rel(Rel) ++ "-pa " ++ Pa), + ?t:run_on_shielded_node(Fun, compat_rel(Rel) ++ "-pa \"" ++ Pa ++ "\""), create_dumps(DataDir,Rels,SlAllocDumps ++ Dumps ++ Acc ++ DosDump); create_dumps(_DataDir,[],Acc) -> Acc. @@ -590,7 +614,8 @@ do_create_dumps(DataDir,Rel) -> case Rel of current -> CD3 = dump_with_args(DataDir,Rel,"instr","+Mim true"), - {SlAllocDumps, [CD1,CD2,CD3], DosDump}; + CD4 = dump_with_strange_module_name(DataDir,Rel,"strangemodname"), + {SlAllocDumps, [CD1,CD2,CD3,CD4], DosDump}; _ -> {SlAllocDumps, [CD1,CD2], DosDump} end. @@ -600,7 +625,7 @@ do_create_dumps(DataDir,Rel) -> %% not connected node, and with monitors and links between nodes. full_dist_dump(DataDir,Rel) -> Opt = rel_opt(Rel), - Pz = "-pz " ++ filename:dirname(code:which(?MODULE)), + Pz = "-pz \"" ++ filename:dirname(code:which(?MODULE)) ++ "\"", PzOpt = [{args,Pz}], {ok,N1} = ?t:start_node(n1,peer,Opt ++ PzOpt), {ok,N2} = ?t:start_node(n2,peer,Opt ++ PzOpt), @@ -648,7 +673,22 @@ dump_with_args(DataDir,Rel,DumpName,Args) -> ?t:stop_node(n1), CD. +%% This dump is added to test OTP-10090 - regarding URL encoding of +%% module names in the module detail link. +dump_with_strange_module_name(DataDir,Rel,DumpName) -> + Opt = rel_opt(Rel), + {ok,N1} = ?t:start_node(n1,peer,Opt), + Mod = '<mod ule#with?strange%name>', + File = atom_to_list(Mod) ++ ".erl", + Forms = [{attribute,1,file,{File,1}}, + {attribute,1,module,Mod}, + {eof,4}], + {ok,Mod,Bin} = rpc:call(N1,compile,forms,[Forms,[binary]]), + {module,Mod} = rpc:call(N1,code,load_binary,[Mod,File,Bin]), + CD = dump(N1,DataDir,Rel,DumpName), + ?t:stop_node(n1), + CD. dump(Node,DataDir,Rel,DumpName) -> rpc:call(Node,erlang,halt,[DumpName]), |