aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer/src/crashdump_viewer_html.erl
diff options
context:
space:
mode:
authorSiri Hansen <[email protected]>2013-10-16 16:53:53 +0200
committerDan Gudmundsson <[email protected]>2014-01-27 16:13:47 +0100
commit41380c0ff6c4fb56aad5702b9d9554ae36580063 (patch)
tree5c16ed3916a700b5f0176d8a33d2ffa28e65c911 /lib/observer/src/crashdump_viewer_html.erl
parent549205db3dee21e83a64a01f03b1e8ed2225b276 (diff)
downloadotp-41380c0ff6c4fb56aad5702b9d9554ae36580063.tar.gz
otp-41380c0ff6c4fb56aad5702b9d9554ae36580063.tar.bz2
otp-41380c0ff6c4fb56aad5702b9d9554ae36580063.zip
observer: improve wx version of crashdump_viewer
* bugfixes * better progress dialogs * show expanded binaries in different formats * speed up reading of big crashdumps
Diffstat (limited to 'lib/observer/src/crashdump_viewer_html.erl')
-rw-r--r--lib/observer/src/crashdump_viewer_html.erl187
1 files changed, 114 insertions, 73 deletions
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 "&lt;" and ">" is changed to "&gt;"
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&lt;"|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&lt;"|Acc],LTB);
+href_proc_port("#Port<"++T,Acc,LTB) ->
+ {Port0,Rest} = split($>,T),
+ Port = "#Port&lt;"++Port0 ++ "&gt;",
+ 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,["&lt;&lt;"|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 = "&lt;" ++ Pid0 ++ "&gt",
+ 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,">"],
- ["&lt;&lt; ",Size," bytes &gt;&gt;"]) | 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 = ["&lt;&lt;",
+ [integer_to_list(X)++"," || X <- PreviewBytes],
+ "...(",
+ observer_lib:to_str({bytes,Size}),
+ ")&gt;&gt;"],
+ if LTB ->
+ href("TARGET=\"expanded\"",
+ ["#Binary?offset="++Offset++
+ "&size="++Size++
+ "&pos="++Pos],
+ PreviewStr);
+ true ->
+ PreviewStr
+ end;
+ _ ->
+ "&lt;&lt; ... &gt;&gt;"
+ 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&lt;"++X++"."++Y++"&gt;",
+ href(Port,Port);
+ Ns ->
+ "#Port&lt;" ++ string:join(Ns,".") ++"...&gt;"
+ 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 = "&lt;"++X++"."++Y++"."++Z++"&gt;",
+ href(Pid,Pid);
+ Ns ->
+ "&lt;" ++ string:join(Ns,".") ++ "...&gt;"
+ 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(
"<FONT COLOR=\"#FF0000\">...(Incomplete Heap)</FONT>")),
- 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(
"<FONT COLOR=\"#FF0000\">&lt;&lt;...(Truncated Binary)&gt;&gt;"
"</FONT>")),
- 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(
"<FONT COLOR=\"#FF0000\">&lt;&lt;...(Nonexisting Binary)&gt;&gt;"
"</FONT>")),
- 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,["&lt;"|Acc],LTB);
+href_proc_port(">"++T,Acc,LTB) ->
+ href_proc_port(T,["&gt;"|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