From 41380c0ff6c4fb56aad5702b9d9554ae36580063 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 16 Oct 2013 16:53:53 +0200 Subject: observer: improve wx version of crashdump_viewer * bugfixes * better progress dialogs * show expanded binaries in different formats * speed up reading of big crashdumps --- lib/observer/src/crashdump_viewer_html.erl | 187 ++++++++++++++++++----------- 1 file changed, 114 insertions(+), 73 deletions(-) (limited to 'lib/observer/src/crashdump_viewer_html.erl') diff --git a/lib/observer/src/crashdump_viewer_html.erl b/lib/observer/src/crashdump_viewer_html.erl index 038126288b..9cd4d6748a 100644 --- a/lib/observer/src/crashdump_viewer_html.erl +++ b/lib/observer/src/crashdump_viewer_html.erl @@ -34,7 +34,7 @@ plain_page/1, info_page/2, proc_details/4, - expanded_memory/2, + expandable_term/3, expanded_binary/1, port/3, internal_ets_tables/2, @@ -492,10 +492,10 @@ format(_Heading,Data) -> %%%----------------------------------------------------------------- %%% Expanded memory -expanded_memory(Heading,Expanded) -> - header(Heading,body(expanded_memory_body(Heading,Expanded))). +expandable_term(Heading,Expanded,Tab) -> + header(Heading,body(expandable_term_body(Heading,Expanded,Tab))). -expanded_memory_body(Heading,[]) -> +expandable_term_body(Heading,[],_Tab) -> [case Heading of "MsgQueue" -> "No messages were found"; "Message Queue" -> "No messages were found"; @@ -504,7 +504,7 @@ expanded_memory_body(Heading,[]) -> "ProcState" -> "Information could not be retrieved," " system messages may not be handled by this process." end]; -expanded_memory_body(Heading,Expanded) -> +expandable_term_body(Heading,Expanded,Tab) -> Attr = "BORDER=0 CELLPADDING=0 CELLSPACING=1 WIDTH=100%", [case Heading of "MsgQueue" -> @@ -513,7 +513,7 @@ expanded_memory_body(Heading,Expanded) -> [th("WIDTH=70%","Message"), th("WIDTH=30%","SeqTraceToken")]) | element(1, lists:mapfoldl(fun(Msg, Even) -> - {msgq_table(Msg, Even), + {msgq_table(Tab, Msg, Even), not Even} end, true, Expanded))]); @@ -523,7 +523,7 @@ expanded_memory_body(Heading,Expanded) -> [th("WIDTH=10%","Id"), th("WIDTH=90%","Message")]) | element(1, lists:mapfoldl(fun(Msg, {Even,N}) -> - {msgq_table(Msg, N, Even), + {msgq_table(Tab, Msg, N, Even), {not Even, N+1}} end, {true,1}, Expanded))]); @@ -533,7 +533,7 @@ expanded_memory_body(Heading,Expanded) -> [th("WIDTH=20%","Label"), th("WIDTH=80%","Term")]) | element(1, lists:mapfoldl(fun(Entry, Even) -> - {stackdump_table(Entry, Even), + {stackdump_table(Tab, Entry, Even), not Even} end, true, Expanded))]); "ProcState" -> @@ -542,7 +542,7 @@ expanded_memory_body(Heading,Expanded) -> [th("WIDTH=20%","Label"), th("WIDTH=80%","Information")]) | element(1, lists:mapfoldl(fun(Entry, Even) -> - {proc_state(Entry,Even), + {proc_state(Tab, Entry,Even), not Even} end, true, Expanded))]); _ -> @@ -551,43 +551,57 @@ expanded_memory_body(Heading,Expanded) -> [th("WIDTH=30%","Key"), th("WIDTH=70%","Value")]) | element(1, lists:mapfoldl(fun(Entry, Even) -> - {dict_table(Entry,Even), + {dict_table(Tab, Entry,Even), not Even} end, true, Expanded))]) end]. -msgq_table({Msg0,Token0}, Even) -> +msgq_table(Tab,{Msg0,Token0}, Even) -> Token = case Token0 of [] -> ""; _ -> io_lib:fwrite("~w",[Token0]) end, - Msg = href_proc_port(lists:flatten(io_lib:format("~p",[Msg0]))), + Msg = all_or_expand(Tab,Msg0), tr(color(Even),[td(pre(Msg)), td(Token)]). -msgq_table(Msg0, Id, Even) -> - Msg = href_proc_port(lists:flatten(io_lib:format("~p",[Msg0]))), +msgq_table(Tab,Msg0, Id, Even) -> + Msg = all_or_expand(Tab,Msg0), tr(color(Even),[td(integer_to_list(Id)), td(pre(Msg))]). -stackdump_table({Label0,Term0},Even) -> +stackdump_table(Tab,{Label0,Term0},Even) -> Label = io_lib:format("~w",[Label0]), - Term = href_proc_port(lists:flatten(io_lib:format("~p",[Term0]))), + Term = all_or_expand(Tab,Term0), tr(color(Even), [td("VALIGN=center",pre(Label)), td(pre(Term))]). -dict_table({Key0,Value0}, Even) -> - Key = href_proc_port(lists:flatten(io_lib:format("~p",[Key0]))), - Value = href_proc_port(lists:flatten(io_lib:format("~p",[Value0]))), +dict_table(Tab,{Key0,Value0}, Even) -> + Key = all_or_expand(Tab,Key0), + Value = all_or_expand(Tab,Value0), tr(color(Even), [td("VALIGN=center",pre(Key)), td(pre(Value))]). -proc_state({Key0,Value0}, Even) -> +proc_state(Tab,{Key0,Value0}, Even) -> Key = lists:flatten(io_lib:format("~s",[Key0])), - Value = href_proc_port(lists:flatten(io_lib:format("~p",[Value0]))), + Value = all_or_expand(Tab,Value0), tr(color(Even), [td("VALIGN=center",Key), td(pre(Value))]). +all_or_expand(Tab,Term) -> + Preview = io_lib:format("~P",[Term,8]), + Check = io_lib:format("~P",[Term,9]), + Exp = Preview=/=Check, + all_or_expand(Tab,Term,Preview,Exp). +all_or_expand(_Tab,_Term,Str,false) -> + href_proc_port(lists:flatten(Str)); +all_or_expand(Tab,Term,Preview,true) -> + Key = {Key1,Key2,Key3} = now(), + ets:insert(Tab,{Key,Term}), + [href_proc_port(lists:flatten(Preview),false), $\n, + href("TARGET=\"expanded\"",["#Term?key1="++integer_to_list(Key1)++ + "&key2="++integer_to_list(Key2)++ + "&key3="++integer_to_list(Key3)], + "Click to expand above term")]. color(true) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_EVEN)); color(false) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_ODD)). - %%%----------------------------------------------------------------- %%% Display an expanded binary, i.e. the whole binary, not just the %%% size of it. @@ -1094,82 +1108,109 @@ br() -> %% In all the following, "<" is changed to "<" and ">" is changed to ">" href_proc_port(Text) -> - href_proc_port(Text,[]). -href_proc_port([$#,$R,$e,$f,$<|T],Acc) -> + href_proc_port(Text,true). +href_proc_port(Text,LinkToBin) -> + href_proc_port(Text,[],LinkToBin). +href_proc_port("#Ref<"++T,Acc,LTB) -> %% No links to refs - href_proc_port(T,[$;,$t,$l,$&,$f,$e,$R,$#|Acc]); -href_proc_port([$#,$F,$u,$n,$<|T],Acc) -> + href_proc_port(T,["#Ref<"|Acc],LTB); +href_proc_port("#Fun<"++T,Acc,LTB) -> %% No links to funs - href_proc_port(T,[$;,$t,$l,$&,$n,$u,$F,$#|Acc]); -href_proc_port([$#,$P,$o,$r,$t,$<|T],Acc) -> - {Port,Rest} = to_gt(T,[$;,$t,$l,$&,$t,$r,$o,$P,$#]), - href_proc_port(Rest,[href(Port,Port)|Acc]); -href_proc_port([$<,$<|T],Acc) -> + href_proc_port(T,["#Fun<"|Acc],LTB); +href_proc_port("#Port<"++T,Acc,LTB) -> + {Port0,Rest} = split($>,T), + Port = "#Port<"++Port0 ++ ">", + href_proc_port(Rest,[href(Port,Port)|Acc],LTB); +href_proc_port("<<"++T,Acc,LTB) -> %% No links to binaries - href_proc_port(T,[$;,$t,$l,$&,$;,$t,$l,$&|Acc]); -href_proc_port([$<,C|T],Acc) when $0 =< C, C =< $9 -> + href_proc_port(T,["<<"|Acc],LTB); +href_proc_port("<"++([C|_]=T),Acc,LTB) when $0 =< C, C =< $9 -> %% Pid - {Pid,Rest} = to_gt(T,[C,$;,$t,$l,$&]), - href_proc_port(Rest,[href(Pid,Pid)|Acc]); -href_proc_port([$",$#,$C,$D,$V,$B,$i,$n,$<|T],Acc) -> + {Pid0,Rest} = split($>,T), + Pid = "<" ++ Pid0 ++ ">", + href_proc_port(Rest,[href(Pid,Pid)|Acc],LTB); +href_proc_port("['#CDVBin'"++T,Acc,LTB) -> %% Binary written by crashdump_viewer:parse_heap_term(...) - {SizeAndPos,[$"|Rest]} = split($>,T), - {Size,Pos} = split($,,SizeAndPos), - href_proc_port(Rest,[href("TARGET=\"expanded\"", - ["#Binary<",Pos,">"], - ["<< ",Size," bytes >>"]) | Acc]); -href_proc_port([$",$#,$C,$D,$V,$P,$o,$r,$t,$<|T],Acc) -> + {OffsetSizePos,Rest} = split($],T), + BinStr = + case string:tokens(OffsetSizePos,",.|") of + [Offset,Size,Pos] -> + Id = {list_to_integer(Offset),10,list_to_integer(Pos)}, + {ok,PreviewBin} = crashdump_viewer:expand_binary(Id), + PreviewBytes = binary_to_list(PreviewBin), + PreviewStr = ["<<", + [integer_to_list(X)++"," || X <- PreviewBytes], + "...(", + observer_lib:to_str({bytes,Size}), + ")>>"], + if LTB -> + href("TARGET=\"expanded\"", + ["#Binary?offset="++Offset++ + "&size="++Size++ + "&pos="++Pos], + PreviewStr); + true -> + PreviewStr + end; + _ -> + "<< ... >>" + end, + href_proc_port(Rest,[BinStr|Acc],LTB); +href_proc_port("['#CDVPort'"++T,Acc,LTB) -> %% Port written by crashdump_viewer:parse_term(...) - {Port,[$"|Rest]} = to_gt(T,[$;,$t,$l,$&,$t,$r,$o,$P,$#]), - href_proc_port(Rest,[href(Port,Port)|Acc]); -href_proc_port([$",$#,$C,$D,$V,$P,$i,$d,$<|T],Acc) -> + {Port0,Rest} = split($],T), + PortStr= + case string:tokens(Port0,",.|") of + [X,Y] -> + Port = "#Port<"++X++"."++Y++">", + href(Port,Port); + Ns -> + "#Port<" ++ string:join(Ns,".") ++"...>" + end, + href_proc_port(Rest,[PortStr|Acc],LTB); +href_proc_port("['#CDVPid'"++T,Acc,LTB) -> %% Pid written by crashdump_viewer:parse_term(...) - {Pid,[$"|Rest]} = to_gt(T,[$;,$t,$l,$&]), - href_proc_port(Rest,[href(Pid,Pid)|Acc]); -href_proc_port([$',$#,$C,$D,$V,$I,$n,$c,$o,$m,$p,$l,$e,$t,$e,$H,$e,$a,$p,$'|T], - Acc)-> + {Pid0,Rest} = split($],T), + PidStr = + case string:tokens(Pid0,",.|") of + [X,Y,Z] -> + Pid = "<"++X++"."++Y++"."++Z++">", + href(Pid,Pid); + Ns -> + "<" ++ string:join(Ns,".") ++ "...>" + end, + href_proc_port(Rest,[PidStr|Acc],LTB); +href_proc_port("'#CDVIncompleteHeap'"++T,Acc,LTB)-> %% The heap is incomplete! Written by crashdump_viewer:deref_pts(...) IH = lists:reverse( lists:flatten( "...(Incomplete Heap)")), - href_proc_port(T,IH++Acc); -href_proc_port([$',$#,$C,$D,$V,$T,$r,$u,$n,$c,$a,$t,$e,$d,$B,$i,$n,$a,$r,$y,$' - |T], Acc)-> + href_proc_port(T,IH++Acc,LTB); +href_proc_port("'#CDVTruncatedBinary'"++T,Acc,LTB)-> %% A binary which is truncated! Written by %% crashdump_viewer:parse_heap_term(...) IH = lists:reverse( lists:flatten( "<<...(Truncated Binary)>>" "")), - href_proc_port(T,IH++Acc); -href_proc_port([$',$#,$C,$D,$V,$N,$o,$n,$e,$x,$i,$s,$t,$i,$n,$g,$B,$i,$n,$a,$r, - $y,$'|T], Acc)-> + href_proc_port(T,IH++Acc,LTB); +href_proc_port("'#CDVNonexistingBinary'"++T,Acc,LTB)-> %% A binary which could not be found in the dump! Written by %% crashdump_viewer:parse_heap_term(...) IH = lists:reverse( lists:flatten( "<<...(Nonexisting Binary)>>" "")), - href_proc_port(T,IH++Acc); -href_proc_port([$<|T],Acc) -> - href_proc_port(T,[$;,$t,$l,$&|Acc]); -href_proc_port([$>|T],Acc) -> - href_proc_port(T,[$;,$t,$g,$&|Acc]); -href_proc_port([H|T],Acc) -> - href_proc_port(T,[H|Acc]); -href_proc_port([],Acc) -> + href_proc_port(T,IH++Acc,LTB); +href_proc_port("<"++T,Acc,LTB) -> + href_proc_port(T,["<"|Acc],LTB); +href_proc_port(">"++T,Acc,LTB) -> + href_proc_port(T,[">"|Acc],LTB); +href_proc_port([H|T],Acc,LTB) -> + href_proc_port(T,[H|Acc],LTB); +href_proc_port([],Acc,_) -> lists:reverse(Acc). -to_gt(Str,Acc) -> - {Match,Rest} = to_gt_noreverse(Str,Acc), - {lists:reverse(Match),Rest}. -to_gt_noreverse([$>|T],Acc) -> - {[$;,$t,$g,$&|Acc],T}; -to_gt_noreverse([H|T],Acc) -> - to_gt_noreverse(T,[H|Acc]); -to_gt_noreverse([],Acc) -> - {Acc,[]}. - split(Char,Str) -> split(Char,Str,[]). split(Char,[Char|Str],Acc) -> % match Char -- cgit v1.2.3