summaryrefslogtreecommitdiff
path: root/lib/observer
diff options
context:
space:
mode:
authorMicael Karlberg <bmk@erlang.org>2021-05-05 11:29:41 +0200
committerMicael Karlberg <bmk@erlang.org>2021-05-12 16:07:46 +0200
commit18164212b4f1c4502a47282fafbae8cba870e980 (patch)
tree9e87dd6c0fd1d137293067e7504d7053ba882b9d /lib/observer
parent87180cfdc73fb90ac1a65f61147450ada9305c1b (diff)
downloaderlang-18164212b4f1c4502a47282fafbae8cba870e980.tar.gz
[observer] Create an 'empty' panel on windows
On platforms that does not support socket (Windows), we create an "empty" panel instead of no panel. That way it will still be possible to check on remote unix-nodes. Also, update the check if OTP version too old (was using port_list instead of socket_list). OTP-17376
Diffstat (limited to 'lib/observer')
-rw-r--r--lib/observer/src/observer_lib.erl82
-rw-r--r--lib/observer/src/observer_sock_wx.erl101
-rw-r--r--lib/observer/src/observer_wx.erl42
3 files changed, 129 insertions, 96 deletions
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 57a9a741a5..c2cc61af86 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -26,7 +26,9 @@
wait_for_progress/0, report_progress/1,
user_term/3, user_term_multiline/3,
interval_dialog/4, start_timer/1, start_timer/2, stop_timer/1, timer_config/1,
- display_info/2, display_info/3, fill_info/2, update_info/2, to_str/1,
+ display_info/2, display_info/3,
+ fill_info/2, fill_info/3, update_info/2,
+ to_str/1,
create_menus/3, create_menu_item/3,
is_darkmode/1, colors/1, create_attrs/1,
set_listctrl_col_size/2, mix/3,
@@ -171,51 +173,65 @@ display_info(Panel, Sizer, Info) ->
end,
[Add(I) || I <- Info].
-fill_info([{dynamic, Key}|Rest], Data)
+fill_info(Fields, Data) ->
+ fill_info(Fields, Data, undefined).
+
+fill_info([{dynamic, Key}|Rest], Data, Default)
when is_atom(Key); is_function(Key) ->
%% Special case used by crashdump_viewer when the value decides
%% which header to use
- case get_value(Key, Data) of
- undefined -> [undefined | fill_info(Rest, Data)];
- {Str,Value} -> [{Str, Value} | fill_info(Rest, Data)]
+ case get_value(Key, Data, Default) of
+ undefined -> [undefined | fill_info(Rest, Data, Default)];
+ {Str,Value} -> [{Str, Value} | fill_info(Rest, Data, Default)]
end;
-fill_info([{Str, Key}|Rest], Data) when is_atom(Key); is_function(Key) ->
- case get_value(Key, Data) of
- undefined -> [undefined | fill_info(Rest, Data)];
- Value -> [{Str, Value} | fill_info(Rest, Data)]
+fill_info([{Str, Key}|Rest], Data, Default)
+ when is_atom(Key); is_function(Key) ->
+ case get_value(Key, Data, Default) of
+ undefined ->
+ [undefined | fill_info(Rest, Data, Default)];
+ Value ->
+ [{Str, Value} | fill_info(Rest, Data, Default)]
end;
-fill_info([{Str,Attrib,Key}|Rest], Data) when is_atom(Key); is_function(Key) ->
- case get_value(Key, Data) of
- undefined -> [undefined | fill_info(Rest, Data)];
- Value -> [{Str,Attrib,Value} | fill_info(Rest, Data)]
+fill_info([{Str, Attrib, Key}|Rest], Data, Default)
+ when is_atom(Key); is_function(Key) ->
+ case get_value(Key, Data, Default) of
+ undefined ->
+ [undefined | fill_info(Rest, Data, Default)];
+ Value ->
+ [{Str,Attrib,Value} | fill_info(Rest, Data, Default)]
end;
-fill_info([{Str, {Format, Key}}|Rest], Data)
+fill_info([{Str, {Format, Key}}|Rest], Data, Default)
when is_atom(Key); is_function(Key) ->
- case get_value(Key, Data) of
- undefined -> [undefined | fill_info(Rest, Data)];
- Value -> [{Str, {Format, Value}} | fill_info(Rest, Data)]
+ case get_value(Key, Data, Default) of
+ undefined -> [undefined | fill_info(Rest, Data, Default)];
+ Value -> [{Str, {Format, Value}} | fill_info(Rest, Data, Default)]
end;
-fill_info([{Str, Attrib, {Format, Key}}|Rest], Data)
+fill_info([{Str, Attrib, {Format, Key}}|Rest], Data, Default)
when is_atom(Key); is_function(Key) ->
- case get_value(Key, Data) of
- undefined -> [undefined | fill_info(Rest, Data)];
- Value -> [{Str, Attrib, {Format, Value}} | fill_info(Rest, Data)]
+ case get_value(Key, Data, Default) of
+ undefined -> [undefined | fill_info(Rest, Data, Default)];
+ Value -> [{Str, Attrib, {Format, Value}} |
+ fill_info(Rest, Data, Default)]
end;
-fill_info([{Str,SubStructure}|Rest], Data) when is_list(SubStructure) ->
- [{Str, fill_info(SubStructure, Data)}|fill_info(Rest,Data)];
-fill_info([{Str,Attrib,SubStructure}|Rest], Data) ->
- [{Str, Attrib, fill_info(SubStructure, Data)}|fill_info(Rest,Data)];
-fill_info([{Str, Key = {K,N}}|Rest], Data) when is_atom(K), is_integer(N) ->
- case get_value(Key, Data) of
- undefined -> [undefined | fill_info(Rest, Data)];
- Value -> [{Str, Value} | fill_info(Rest, Data)]
+fill_info([{Str,SubStructure}|Rest], Data, Default)
+ when is_list(SubStructure) ->
+ [{Str, fill_info(SubStructure, Data, Default)}|
+ fill_info(Rest, Data, Default)];
+fill_info([{Str,Attrib,SubStructure}|Rest], Data, Default) ->
+ [{Str, Attrib, fill_info(SubStructure, Data, Default)}|
+ fill_info(Rest, Data, Default)];
+fill_info([{Str, Key = {K,N}}|Rest], Data, Default)
+ when is_atom(K), is_integer(N) ->
+ case get_value(Key, Data, Default) of
+ undefined -> [undefined | fill_info(Rest, Data, Default)];
+ Value -> [{Str, Value} | fill_info(Rest, Data, Default)]
end;
-fill_info([], _) -> [].
+fill_info([], _, _Default) -> [].
-get_value(Fun, Data) when is_function(Fun) ->
+get_value(Fun, Data, _Default) when is_function(Fun) ->
Fun(Data);
-get_value(Key, Data) ->
- proplists:get_value(Key,Data).
+get_value(Key, Data, Default) ->
+ proplists:get_value(Key, Data, Default).
update_info([Fields|Fs], [{_Header, SubStructure}| Rest]) ->
update_info2(Fields, SubStructure),
diff --git a/lib/observer/src/observer_sock_wx.erl b/lib/observer/src/observer_sock_wx.erl
index dc91d0f050..c1cb161a8a 100644
--- a/lib/observer/src/observer_sock_wx.erl
+++ b/lib/observer/src/observer_sock_wx.erl
@@ -75,7 +75,7 @@
panel,
sizer,
fields,
- node = {node(),true},
+ node = {node(), true},
opt = #opt{},
right_clicked_socket,
sockets,
@@ -111,7 +111,9 @@ update_gen_socket_info(#state{node = {Node, true},
case rpc:call(Node, observer_backend, socket_info, []) of
Info when is_list(Info) ->
Gen = info_fields(),
- observer_lib:update_info(Fields, observer_lib:fill_info(Gen, Info)),
+ observer_lib:update_info(Fields,
+ observer_lib:fill_info(Gen, Info,
+ "Not Supported")),
wxSizer:layout(Sizer);
_ ->
ignore
@@ -129,17 +131,35 @@ init([Notebook, Parent, Config]) ->
"~n Notebook: ~p"
"~n Parent: ~p"
"~n Config: ~p", [Notebook, Parent, Config]),
- Info = observer_backend:socket_info(),
+ try
+ begin
+ do_init(Notebook, Parent, Config, observer_backend:socket_info())
+ end
+ catch
+ C:E:S ->
+ %% Current node does not support socket (windows?)
+ d("init -> catched: "
+ "~n C: ~p"
+ "~n E: ~p"
+ "~n S: ~p", [C, E, S]),
+ do_init(Notebook, Parent, Config, [])
+ end.
+
+do_init(Notebook, Parent, Config, Info) ->
Gen = info_fields(),
Panel = wxPanel:new(Notebook),
Sizer = wxBoxSizer:new(?wxVERTICAL),
GenSizer = wxBoxSizer:new(?wxHORIZONTAL),
{GenPanel, _GenSizer, GenFields} =
- observer_lib:display_info(Panel, observer_lib:fill_info(Gen, Info)),
- wxSizer:add(GenSizer, GenPanel, [{flag, ?wxEXPAND}, {proportion, 1}]),
+ observer_lib:display_info(Panel,
+ observer_lib:fill_info(Gen, Info,
+ "Not Supported")),
+ wxSizer:add(GenSizer, GenPanel,
+ [{flag, ?wxEXPAND}, {proportion, 1}]),
BorderFlags = ?wxLEFT bor ?wxRIGHT,
- wxSizer:add(Sizer, GenSizer, [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP},
- {proportion, 0}, {border, 5}]),
+ wxSizer:add(Sizer, GenSizer,
+ [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP},
+ {proportion, 0}, {border, 5}]),
Style = ?wxLC_REPORT bor ?wxLC_HRULES,
Grid = wxListCtrl:new(Panel, [{winid, ?GRID}, {style, Style}]),
wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL},
@@ -371,43 +391,6 @@ handle_call(Event, From, _State) ->
handle_cast(Event, _State) ->
error({unhandled_cast, Event}).
-%% handle_info({socketinfo_open, IdStr},
-%% State = #state{node = {ActiveNodeName, ActiveAvailable},
-%% grid = Grid,
-%% opt = Opt,
-%% open_wins = Opened}) ->
-%% NodeName = node(list_to_port(PortIdStr)), % How do we do this for sockets?
-%% Available =
-%% case NodeName of
-%% ActiveNodeName ->
-%% ActiveAvailable;
-%% _ ->
-%% portinfo_available(NodeName)
-%% end,
-%% if Available ->
-%% Sockets0 = get_sockets(NodeName, Available),
-%% Sockets = lists:keyfind(SockIdStr, #socket.id_str, Sockets),
-%% NewOpened =
-%% case Port of
-%% false ->
-%% self() ! {error,"No such socket: " ++ SockIdStr},
-%% Opened;
-%% _ ->
-%% display_socket_info(Grid, Port, Opened)
-%% end,
-%% Ports =
-%% case NodeName of
-%% ActiveNodeName ->
-%% update_grid(Grid, sel(State), Opt, Ports0);
-%% _ ->
-%% State#state.ports
-%% end,
-%% {noreply, State#state{ports=Ports, open_wins=NewOpened}};
-%% true ->
-%% popup_unavailable_info(NodeName),
-%% {noreply, State}
-%% end;
-
handle_info(refresh_interval, State = #state{node = Node,
grid = Grid,
opt = Opt,
@@ -425,12 +408,13 @@ handle_info(refresh_interval, State = #state{node = Node,
{noreply, State#state{sockets = Sockets}}
end;
-handle_info({active, NodeName},
+handle_info({active, NodeName},
#state{parent = Parent,
grid = Grid,
opt = Opt,
timer = Timer0} = State0) ->
- Available = portinfo_available(NodeName),
+ d("handle_info({active, ~p}) -> entry", [NodeName]),
+ Available = socketinfo_available(NodeName),
Available orelse popup_unavailable_info(NodeName),
State1 = State0#state{node = {NodeName, Available}},
_ = update_gen_socket_info(State1),
@@ -446,15 +430,18 @@ handle_info(not_active, State = #state{timer = Timer0}) ->
Timer = observer_lib:stop_timer(Timer0),
{noreply, State#state{timer=Timer}};
-handle_info({info, {port_info_not_available,NodeName}},
+handle_info({info, {socket_info_not_available, NodeName}},
State = #state{panel=Panel}) ->
- Str = io_lib:format("Can not fetch port info from ~p.~n"
- "Too old OTP version.",[NodeName]),
+ Str = io_lib:format("Can not fetch socket info from ~p.~n"
+ "Too old OTP version.", [NodeName]),
observer_lib:display_info_dialog(Panel, Str),
{noreply, State};
handle_info({error, Error}, #state{panel=Panel} = State) ->
- Str = io_lib:format("ERROR: ~ts~n",[Error]),
+ ErrorStr = if is_list(Error) -> Error;
+ true -> f("~p", [Error])
+ end,
+ Str = io_lib:format("ERROR: ~ts~n", [ErrorStr]),
observer_lib:display_info_dialog(Panel, Str),
{noreply, State};
@@ -492,9 +479,17 @@ get_sockets(NodeName) when is_atom(NodeName) ->
case rpc:call(NodeName, observer_backend, get_socket_list, []) of
SocketInfoMaps when is_list(SocketInfoMaps) ->
[infomap_to_rec(SockInfo) || SockInfo <- SocketInfoMaps];
+ {badrpc,
+ {'EXIT', {undef, [{observer_backend, get_socket_list, [], []}]}}} ->
+ d("get_sockets -> No Backend"),
+ {error, "No socket backend support"};
{badrpc, Error} ->
+ d("get_sockets -> badrpc: "
+ "~n ~p", [Error]),
{error, Error};
{error, _} = ERROR ->
+ d("get_sockets -> error:"
+ "~n ~p", [ERROR]),
ERROR
end.
@@ -792,16 +787,16 @@ get_indecies(Rest = [_|_], I, [_|T]) ->
get_indecies(_, _, _) ->
[].
-portinfo_available(NodeName) ->
+socketinfo_available(NodeName) ->
_ = rpc:call(NodeName, code, ensure_loaded, [observer_backend]),
case rpc:call(NodeName, erlang, function_exported,
- [observer_backend, get_port_list, 0]) of
+ [observer_backend, get_socket_list, 0]) of
true -> true;
false -> false
end.
popup_unavailable_info(NodeName) ->
- self() ! {info, {port_info_not_available, NodeName}},
+ self() ! {info, {socket_info_not_available, NodeName}},
ok.
f(F, A) ->
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 04f725cb4e..b61532f5b0 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -200,8 +200,26 @@ setup(#state{frame = Frame} = State) ->
wxNotebook:addPage(Notebook, PortPanel, "Ports", []),
%% Socket Panel
+ %% Note that this panel should *only* be added if we have support for socket
+ %% Also, if we run on/with an "old" node (without this support),
+ %% we should also not include this!
+ %% SockPanel =
+ %% try socket:supports() of
+ %% _ ->
+ %% d("setup -> create socket panel"),
+ %% SP = observer_sock_wx:start_link(Notebook,
+ %% self(),
+ %% Cnf(sock_panel)),
+ %% wxNotebook:addPage(Notebook, SP, "Sockets", []),
+ %% SP
+ %% catch
+ %% _:_:_ ->
+ %% undefined
+ %% end,
d("setup -> create socket panel"),
- SockPanel = observer_sock_wx:start_link(Notebook, self(), Cnf(sock_panel)),
+ SockPanel = observer_sock_wx:start_link(Notebook,
+ self(),
+ Cnf(sock_panel)),
wxNotebook:addPage(Notebook, SockPanel, "Sockets", []),
%% Table Viewer Panel
@@ -226,15 +244,19 @@ setup(#state{frame = Frame} = State) ->
SysPid = wx_object:get_pid(SysPanel),
SysPid ! {active, node()},
- Panels = [{sys_panel, SysPanel, "System"}, %% In order
- {perf_panel, PerfPanel, "Load Charts"},
- {allc_panel, AllcPanel, ?ALLOC_STR},
- {app_panel, AppPanel, "Applications"},
- {pro_panel, ProPanel, "Processes"},
- {port_panel, PortPanel, "Ports"},
- {sock_panel, SockPanel, "Sockets"},
- {tv_panel, TVPanel, "Table Viewer"},
- {trace_panel, TracePanel, ?TRACE_STR}],
+ Panels =
+ [{sys_panel, SysPanel, "System"}, %% In order
+ {perf_panel, PerfPanel, "Load Charts"},
+ {allc_panel, AllcPanel, ?ALLOC_STR},
+ {app_panel, AppPanel, "Applications"},
+ {pro_panel, ProPanel, "Processes"},
+ {port_panel, PortPanel, "Ports"}] ++
+ if (SockPanel =:= undefined) -> [];
+ true ->
+ [{sock_panel, SockPanel, "Sockets"}]
+ end ++
+ [{tv_panel, TVPanel, "Table Viewer"},
+ {trace_panel, TracePanel, ?TRACE_STR}],
UpdState = State#state{main_panel = Panel,
notebook = Notebook,