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]),  | 
