aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer
diff options
context:
space:
mode:
authorSiri Hansen <[email protected]>2017-09-13 15:26:55 +0200
committerSiri Hansen <[email protected]>2017-09-18 11:07:08 +0200
commit9ee5ad4f99f2540860f5acb357aac6b08d7e494e (patch)
treeedcde8e08a77dd474e8275e96c71a53ee30b4510 /lib/observer
parentae089c72fb06b069675cbebcec10f0820cf16112 (diff)
downloadotp-9ee5ad4f99f2540860f5acb357aac6b08d7e494e.tar.gz
otp-9ee5ad4f99f2540860f5acb357aac6b08d7e494e.tar.bz2
otp-9ee5ad4f99f2540860f5acb357aac6b08d7e494e.zip
cdv: Show progress bar while reading big data
Diffstat (limited to 'lib/observer')
-rw-r--r--lib/observer/src/cdv_detail_wx.erl28
-rw-r--r--lib/observer/src/cdv_html_wx.erl9
-rw-r--r--lib/observer/src/cdv_term_cb.erl7
-rw-r--r--lib/observer/src/cdv_virtual_list_wx.erl2
-rw-r--r--lib/observer/src/cdv_wx.erl3
-rw-r--r--lib/observer/src/crashdump_viewer.erl36
-rw-r--r--lib/observer/src/observer_lib.erl83
-rw-r--r--lib/observer/src/observer_procinfo.erl2
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl5
9 files changed, 132 insertions, 43 deletions
diff --git a/lib/observer/src/cdv_detail_wx.erl b/lib/observer/src/cdv_detail_wx.erl
index 4c26e447a6..f6d282638a 100644
--- a/lib/observer/src/cdv_detail_wx.erl
+++ b/lib/observer/src/cdv_detail_wx.erl
@@ -20,7 +20,7 @@
-behaviour(wx_object).
--export([start_link/4]).
+-export([start_link/5]).
-export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3,
handle_call/3, handle_info/2]).
@@ -39,27 +39,42 @@
-define(ID_NOTEBOOK, 604).
%% Detail view
-start_link(Id, Data, ParentFrame, Callback) ->
- wx_object:start_link(?MODULE, [Id, Data, ParentFrame, Callback, self()], []).
+start_link(Id, Data, ParentFrame, Callback, App) ->
+ wx_object:start_link(?MODULE,[Id,Data,ParentFrame,Callback,App,self()],[]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-init([Id, Data, ParentFrame, Callback, Parent]) ->
+init([Id, Data, ParentFrame, Callback, App, Parent]) ->
+ display_progress(ParentFrame,App),
case Callback:get_details(Id, Data) of
{ok,Details} ->
- init(Id,ParentFrame,Callback,Parent,Details);
+ init(Id,ParentFrame,Callback,App,Parent,Details);
{yes_no, Info, Fun} ->
+ destroy_progress(App),
case observer_lib:display_yes_no_dialog(Info) of
?wxID_YES -> Fun();
?wxID_NO -> ok
end,
{stop,normal};
{info,Info} ->
+ destroy_progress(App),
observer_lib:display_info_dialog(ParentFrame,Info),
{stop,normal}
end.
-init(Id,ParentFrame,Callback,Parent,{Title,Info,TW}) ->
+%% Display progress bar only if the calling app is crashdump_viewer
+display_progress(ParentFrame,cdv) ->
+ observer_lib:display_progress_dialog(ParentFrame,
+ "Crashdump Viewer",
+ "Reading data");
+display_progress(_,_) ->
+ ok.
+destroy_progress(cdv) ->
+ observer_lib:destroy_progress_dialog();
+destroy_progress(_) ->
+ ok.
+
+init(Id,ParentFrame,Callback,App,Parent,{Title,Info,TW}) ->
Frame=wxFrame:new(ParentFrame, ?wxID_ANY, [Title],
[{style, ?wxDEFAULT_FRAME_STYLE}, {size, {850,600}}]),
MenuBar = wxMenuBar:new(),
@@ -88,6 +103,7 @@ init(Id,ParentFrame,Callback,Parent,{Title,Info,TW}) ->
wxFrame:connect(Frame, close_window),
wxMenu:connect(Frame, command_menu_selected),
wxFrame:show(Frame),
+ destroy_progress(App),
{Frame, #state{parent=Parent,
id=Id,
frame=Frame,
diff --git a/lib/observer/src/cdv_html_wx.erl b/lib/observer/src/cdv_html_wx.erl
index 5158e95a65..4b43b6a840 100644
--- a/lib/observer/src/cdv_html_wx.erl
+++ b/lib/observer/src/cdv_html_wx.erl
@@ -52,8 +52,12 @@ init([ParentWin, HtmlText]) ->
init(ParentWin, HtmlText, undefined, cdv).
init(ParentWin, HtmlText, Tab, App) ->
+ %% If progress dialog is shown, remove it now - and sett cursor busy instead
+ observer_lib:destroy_progress_dialog(),
+ wx_misc:beginBusyCursor(),
HtmlWin = observer_lib:html_window(ParentWin),
wxHtmlWindow:setPage(HtmlWin,HtmlText),
+ wx_misc:endBusyCursor(),
{HtmlWin, #state{panel=HtmlWin,expand_table=Tab,app=App}}.
%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -123,11 +127,12 @@ handle_event(Event, State) ->
%%%-----------------------------------------------------------------
%%% Internal
-expand(Id,Callback,#state{expand_wins=Opened0}=State) ->
+expand(Id,Callback,#state{expand_wins=Opened0, app=App}=State) ->
Opened =
case lists:keyfind(Id,1,Opened0) of
false ->
- EW = cdv_detail_wx:start_link(Id,[],State#state.panel,Callback),
+ EW = cdv_detail_wx:start_link(Id,[],State#state.panel,
+ Callback,App),
wx_object:get_pid(EW) ! active,
[{Id,EW}|Opened0];
{_,EW} ->
diff --git a/lib/observer/src/cdv_term_cb.erl b/lib/observer/src/cdv_term_cb.erl
index f206d7e4c9..24fc33267e 100644
--- a/lib/observer/src/cdv_term_cb.erl
+++ b/lib/observer/src/cdv_term_cb.erl
@@ -30,8 +30,11 @@ detail_pages() ->
[{"Term", fun init_term_page/2}].
init_term_page(ParentWin, {Type, [Term, Tab]}) ->
+ observer_lib:report_progress({ok,"Expanding term"}),
+ observer_lib:report_progress({ok,start_pulse}),
Expanded = expand(Term, true),
BinSaved = expand(Term, Tab),
+ observer_lib:report_progress({ok,stop_pulse}),
cdv_multi_wx:start_link(
ParentWin,
[{"Format \~p",cdv_html_wx,{Type, format_term_fun("~p",BinSaved,Tab)}},
@@ -42,11 +45,15 @@ init_term_page(ParentWin, {Type, [Term, Tab]}) ->
format_term_fun(Format,Term,Tab) ->
fun() ->
+ observer_lib:report_progress({ok,"Formatting term"}),
+ observer_lib:report_progress({ok,start_pulse}),
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)
+ after
+ observer_lib:report_progress({ok,stop_pulse})
end
end.
diff --git a/lib/observer/src/cdv_virtual_list_wx.erl b/lib/observer/src/cdv_virtual_list_wx.erl
index f3daae8e4d..33c0c880b1 100644
--- a/lib/observer/src/cdv_virtual_list_wx.erl
+++ b/lib/observer/src/cdv_virtual_list_wx.erl
@@ -174,7 +174,7 @@ do_start_detail_win(Id, #state{panel=Panel,detail_wins=Opened,
case lists:keyfind(Id, 1, Opened) of
false ->
Data = call(Holder, {get_data, self(), Id}),
- case cdv_detail_wx:start_link(Id, Data, Panel, Callback) of
+ case cdv_detail_wx:start_link(Id, Data, Panel, Callback, cdv) of
{error, _} -> Opened;
IW -> [{Id, IW} | Opened]
end;
diff --git a/lib/observer/src/cdv_wx.erl b/lib/observer/src/cdv_wx.erl
index 898f39ded2..ffd7694ab3 100644
--- a/lib/observer/src/cdv_wx.erl
+++ b/lib/observer/src/cdv_wx.erl
@@ -412,7 +412,8 @@ load_dump(Frame,undefined) ->
error
end;
load_dump(Frame,FileName) ->
- ok = observer_lib:display_progress_dialog("Crashdump Viewer",
+ ok = observer_lib:display_progress_dialog(wx:null(),
+ "Crashdump Viewer",
"Loading crashdump"),
crashdump_viewer:read_file(FileName),
case observer_lib:wait_for_progress() of
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index 0534ead50e..b01c3a1bac 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -86,6 +86,7 @@
-define(max_line_size,100). % max number of bytes (i.e. characters) the
% line_head/1 function can return
-define(not_available,"N/A").
+-define(binary_size_progress_limit,10000).
%% All possible tags - use macros in order to avoid misspelling in the code
@@ -307,6 +308,7 @@ init([]) ->
ets:new(cdv_dump_index_table,[ordered_set,named_table,public]),
ets:new(cdv_reg_proc_table,[ordered_set,named_table,public]),
ets:new(cdv_binary_index_table,[ordered_set,named_table,public]),
+ ets:new(cdv_heap_file_chars,[ordered_set,named_table,public]),
{ok, #state{}}.
%%--------------------------------------------------------------------
@@ -849,7 +851,12 @@ indexify(Fd,AddrAdj,Bin,N) ->
_ ->
insert_index(Tag,Id,NewPos)
end,
- put_last_tag(Tag,Id),
+ case put_last_tag(Tag,Id) of
+ {?proc_heap,LastId} ->
+ [{_,LastPos}] = lookup_index(?proc_heap,LastId),
+ ets:insert(cdv_heap_file_chars,{LastId,N+Start+1-LastPos});
+ _ -> ok
+ end,
indexify(Fd,AddrAdj,Rest,N1);
nomatch ->
case progress_read(Fd) of
@@ -1485,6 +1492,8 @@ parse_dictionary(Line0, BinAddrAdj, D) ->
read_heap(Fd,Pid,BinAddrAdj,Dict0) ->
case lookup_index(?proc_heap,Pid) of
[{_,Pos}] ->
+ [{_,Chars}] = ets:lookup(cdv_heap_file_chars,Pid),
+ init_progress("Reading process heap",Chars),
pos_bof(Fd,Pos),
read_heap(BinAddrAdj,Dict0);
[] ->
@@ -1495,13 +1504,16 @@ read_heap(BinAddrAdj,Dict0) ->
%% This function is never called if the dump is truncated in {?proc_heap,Pid}
case get(fd) of
end_of_heap ->
+ end_progress(),
Dict0;
Fd ->
case bytes(Fd) of
"=" ++ _next_tag ->
+ end_progress(),
put(fd, end_of_heap),
Dict0;
Line ->
+ update_progress(length(Line)+1),
Dict = parse(Line,BinAddrAdj,Dict0),
read_heap(BinAddrAdj,Dict)
end
@@ -2664,6 +2676,7 @@ deref_ptr(Ptr, Line, BinAddrAdj, D0) ->
put(fd, end_of_heap),
deref_ptr(Ptr, Line, BinAddrAdj, D0);
L ->
+ update_progress(length(L)+1),
D = parse(L, BinAddrAdj, D0),
deref_ptr(Ptr, Line, BinAddrAdj, D)
end
@@ -2731,7 +2744,7 @@ get_label([H|T], Acc) ->
get_binary(Line0) ->
case get_hex(Line0) of
{N,":"++Line} ->
- do_get_binary(N, Line, []);
+ do_get_binary(N, Line, [], false);
_ ->
{'#CDVTruncatedBinary',[]}
end.
@@ -2739,17 +2752,23 @@ get_binary(Line0) ->
get_binary(Offset,Size,Line0) ->
case get_hex(Line0) of
{_N,":"++Line} ->
- do_get_binary(Size, lists:sublist(Line,(Offset*2)+1,Size*2), []);
+ Progress = Size>?binary_size_progress_limit,
+ Progress andalso init_progress("Reading binary",Size),
+ do_get_binary(Size, lists:sublist(Line,(Offset*2)+1,Size*2), [],
+ Progress);
_ ->
{'#CDVTruncatedBinary',[]}
end.
-do_get_binary(0, Line, Acc) ->
+do_get_binary(0, Line, Acc, Progress) ->
+ Progress andalso end_progress(),
{list_to_binary(lists:reverse(Acc)),Line};
-do_get_binary(N, [A,B|Line], Acc) ->
+do_get_binary(N, [A,B|Line], Acc, Progress) ->
Byte = (get_hex_digit(A) bsl 4) bor get_hex_digit(B),
- do_get_binary(N-1, Line, [Byte|Acc]);
-do_get_binary(_N, [], _Acc) ->
+ Progress andalso update_progress(),
+ do_get_binary(N-1, Line, [Byte|Acc], Progress);
+do_get_binary(_N, [], _Acc, Progress) ->
+ Progress andalso end_progress(),
{'#CDVTruncatedBinary',[]}.
cdvbin(Offset,Size,{'#CDVBin',Pos}) ->
@@ -2766,7 +2785,8 @@ cdvbin(_,_,'#CDVNonexistingBinary') ->
reset_tables() ->
ets:delete_all_objects(cdv_dump_index_table),
ets:delete_all_objects(cdv_reg_proc_table),
- ets:delete_all_objects(cdv_binary_index_table).
+ ets:delete_all_objects(cdv_binary_index_table),
+ ets:delete_all_objects(cdv_heap_file_chars).
insert_index(Tag,Id,Pos) ->
ets:insert(cdv_dump_index_table,{{Tag,Pos},Id}).
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 463fb5b8ef..29f4f9fabc 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -21,7 +21,7 @@
-export([get_wx_parent/1,
display_info_dialog/2, display_yes_no_dialog/1,
- display_progress_dialog/2, destroy_progress_dialog/0,
+ display_progress_dialog/3, destroy_progress_dialog/0,
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,
@@ -40,6 +40,7 @@
-define(SINGLE_LINE_STYLE, ?wxBORDER_NONE bor ?wxTE_READONLY bor ?wxTE_RICH2).
-define(MULTI_LINE_STYLE, ?SINGLE_LINE_STYLE bor ?wxTE_MULTILINE).
+-define(pulse_timeout,50).
get_wx_parent(Window) ->
Parent = wxWindow:getParent(Window),
@@ -688,11 +689,11 @@ create_status_bar(Panel) ->
%%%-----------------------------------------------------------------
%%% Progress dialog
-define(progress_handler,cdv_progress_handler).
-display_progress_dialog(Title,Str) ->
+display_progress_dialog(Parent,Title,Str) ->
Caller = self(),
Env = wx:get_env(),
spawn_link(fun() ->
- progress_handler(Caller,Env,Title,Str)
+ progress_handler(Caller,Env,Parent,Title,Str)
end),
ok.
@@ -716,31 +717,38 @@ report_progress(Progress) ->
ok
end.
-progress_handler(Caller,Env,Title,Str) ->
+progress_handler(Caller,Env,Parent,Title,Str) ->
register(?progress_handler,self()),
wx:set_env(Env),
- PD = progress_dialog(Env,Title,Str),
- try progress_loop(Title,PD,Caller)
+ PD = progress_dialog(Env,Parent,Title,Str),
+ try progress_loop(Title,PD,Caller,infinity)
catch closed -> normal end.
-progress_loop(Title,PD,Caller) ->
+progress_loop(Title,PD,Caller,Pulse) ->
receive
{progress,{ok,done}} -> % to make wait_for_progress/0 return
Caller ! continue,
- progress_loop(Title,PD,Caller);
+ progress_loop(Title,PD,Caller,Pulse);
+ {progress,{ok,start_pulse}} ->
+ update_progress_pulse(PD),
+ progress_loop(Title,PD,Caller,?pulse_timeout);
+ {progress,{ok,stop_pulse}} ->
+ progress_loop(Title,PD,Caller,infinity);
{progress,{ok,Percent}} when is_integer(Percent) ->
update_progress(PD,Percent),
- progress_loop(Title,PD,Caller);
+ progress_loop(Title,PD,Caller,Pulse);
{progress,{ok,Msg}} ->
update_progress_text(PD,Msg),
- progress_loop(Title,PD,Caller);
+ progress_loop(Title,PD,Caller,Pulse);
{progress,{error, Reason}} ->
+ {Dialog,_,_} = PD,
+ Parent = wxWindow:getParent(Dialog),
finish_progress(PD),
FailMsg =
if is_list(Reason) -> Reason;
true -> file:format_error(Reason)
end,
- display_info_dialog(PD,"Crashdump Viewer Error",FailMsg),
+ display_info_dialog(Parent,"Crashdump Viewer Error",FailMsg),
Caller ! error,
unregister(?progress_handler),
unlink(Caller);
@@ -748,28 +756,57 @@ progress_loop(Title,PD,Caller) ->
finish_progress(PD),
unregister(?progress_handler),
unlink(Caller)
+ after Pulse ->
+ update_progress_pulse(PD),
+ progress_loop(Title,PD,Caller,?pulse_timeout)
end.
-progress_dialog(_Env,Title,Str) ->
- PD = wxProgressDialog:new(Title,Str,
- [{maximum,101},
- {style,
- ?wxPD_APP_MODAL bor
- ?wxPD_SMOOTH bor
- ?wxPD_AUTO_HIDE}]),
- wxProgressDialog:setMinSize(PD,{200,-1}),
- PD.
+progress_dialog(_Env,Parent,Title,Str) ->
+ progress_dialog_new(Parent,Title,Str).
update_progress(PD,Value) ->
- try wxProgressDialog:update(PD,Value)
+ try progress_dialog_update(PD,Value)
catch _:_ -> throw(closed) %% Port or window have died
end.
update_progress_text(PD,Text) ->
- try wxProgressDialog:update(PD,0,[{newmsg,Text}])
+ try progress_dialog_update(PD,Text)
+ catch _:_ -> throw(closed) %% Port or window have died
+ end.
+update_progress_pulse(PD) ->
+ try progress_dialog_pulse(PD)
catch _:_ -> throw(closed) %% Port or window have died
end.
finish_progress(PD) ->
- wxProgressDialog:destroy(PD).
+ try progress_dialog_update(PD,100)
+ catch _:_ -> ok
+ after progress_dialog_destroy(PD)
+ end.
+
+progress_dialog_new(Parent,Title,Str) ->
+ Dialog = wxDialog:new(Parent, ?wxID_ANY, Title,
+ [{style,?wxDEFAULT_DIALOG_STYLE}]),
+ Panel = wxPanel:new(Dialog),
+ Sizer = wxBoxSizer:new(?wxVERTICAL),
+ Message = wxStaticText:new(Panel, 1, Str),
+ Gauge = wxGauge:new(Panel, 2, 100, [{size, {170, -1}},
+ {style, ?wxGA_HORIZONTAL}]),
+ SizerFlags = ?wxEXPAND bor ?wxLEFT bor ?wxRIGHT bor ?wxTOP,
+ wxSizer:add(Sizer, Message, [{flag,SizerFlags},{border,15}]),
+ wxSizer:add(Sizer, Gauge, [{flag, SizerFlags bor ?wxBOTTOM},{border,15}]),
+ wxPanel:setSizer(Panel, Sizer),
+ wxSizer:setSizeHints(Sizer, Dialog),
+ wxDialog:show(Dialog),
+ {Dialog,Message,Gauge}.
+
+progress_dialog_update({_,_,Gauge},Value) when is_integer(Value) ->
+ wxGauge:setValue(Gauge,Value);
+progress_dialog_update({_,Message,Gauge},Text) when is_list(Text) ->
+ wxGauge:setValue(Gauge,0),
+ wxStaticText:setLabel(Message,Text).
+progress_dialog_pulse({_,_,Gauge}) ->
+ wxGauge:pulse(Gauge).
+progress_dialog_destroy({Dialog,_,_}) ->
+ wxDialog:destroy(Dialog).
make_obsbin(Bin,Tab) ->
Size = byte_size(Bin),
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index 963def958b..fb02ae2728 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -151,7 +151,7 @@ handle_event(#wx{event=#wxHtmlLink{linkInfo=#wxHtmlLinkInfo{href=Href}}},
Opened =
case lists:keyfind(Id,1,Opened0) of
false ->
- Win = cdv_detail_wx:start_link(Id,[],Frame,Callback),
+ Win = cdv_detail_wx:start_link(Id,[],Frame,Callback,obs),
[{Id,Win}|Opened0];
{_,Win} ->
wxFrame:raise(Win),
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 4449ec54d1..f9ac884743 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -574,7 +574,10 @@ do_create_dumps(DataDir,Rel) ->
current ->
CD3 = dump_with_args(DataDir,Rel,"instr","+Mim true"),
CD4 = dump_with_strange_module_name(DataDir,Rel,"strangemodname"),
- Bytes = rand:uniform(300000) + 100,
+ Tmp = dump_with_args(DataDir,Rel,"trunc_bytes",""),
+ {ok,#file_info{size=Max}} = file:read_file_info(Tmp),
+ ok = file:delete(Tmp),
+ Bytes = max(15,rand:uniform(Max)),
CD5 = dump_with_args(DataDir,Rel,"trunc_bytes",
"-env ERL_CRASH_DUMP_BYTES " ++
integer_to_list(Bytes)),