From 9ee5ad4f99f2540860f5acb357aac6b08d7e494e Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 13 Sep 2017 15:26:55 +0200 Subject: cdv: Show progress bar while reading big data --- lib/observer/src/cdv_detail_wx.erl | 28 ++++++++-- lib/observer/src/cdv_html_wx.erl | 9 ++- lib/observer/src/cdv_term_cb.erl | 7 +++ lib/observer/src/cdv_virtual_list_wx.erl | 2 +- lib/observer/src/cdv_wx.erl | 3 +- lib/observer/src/crashdump_viewer.erl | 36 +++++++++--- lib/observer/src/observer_lib.erl | 83 ++++++++++++++++++++-------- lib/observer/src/observer_procinfo.erl | 2 +- lib/observer/test/crashdump_viewer_SUITE.erl | 5 +- 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)), -- cgit v1.2.3