diff options
Diffstat (limited to 'lib/observer/src/crashdump_viewer.erl')
-rw-r--r-- | lib/observer/src/crashdump_viewer.erl | 237 |
1 files changed, 187 insertions, 50 deletions
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl index 14b086ff58..0627c15b1c 100644 --- a/lib/observer/src/crashdump_viewer.erl +++ b/lib/observer/src/crashdump_viewer.erl @@ -74,6 +74,7 @@ loaded_modules/0, loaded_mod_details/1, memory/0, + persistent_terms/0, allocated_areas/0, allocator_info/0, hash_tables/0, @@ -139,6 +140,7 @@ -define(node,node). -define(not_connected,not_connected). -define(old_instr_data,old_instr_data). +-define(persistent_terms,persistent_terms). -define(port,port). -define(proc,proc). -define(proc_dictionary,proc_dictionary). @@ -293,6 +295,8 @@ loaded_mod_details(Mod) -> call({loaded_mod_details,Mod}). memory() -> call(memory). +persistent_terms() -> + call(persistent_terms). allocated_areas() -> call(allocated_areas). allocator_info() -> @@ -471,6 +475,11 @@ handle_call(memory,_From,State=#state{file=File}) -> Memory=memory(File), TW = truncated_warning([?memory]), {reply,{ok,Memory,TW},State}; +handle_call(persistent_terms,_From,State=#state{file=File,dump_vsn=DumpVsn}) -> + TW = truncated_warning([?persistent_terms,?literals]), + DecodeOpts = get_decode_opts(DumpVsn), + Terms = persistent_terms(File, DecodeOpts), + {reply,{ok,Terms,TW},State}; handle_call(allocated_areas,_From,State=#state{file=File}) -> AllocatedAreas=allocated_areas(File), TW = truncated_warning([?allocated_areas]), @@ -1444,15 +1453,7 @@ maybe_other_node2(Channel) -> expand_memory(Fd,Pid,DumpVsn) -> DecodeOpts = get_decode_opts(DumpVsn), put(fd,Fd), - Dict0 = case get(?literals) of - undefined -> - Literals = read_literals(Fd,DecodeOpts), - put(?literals,Literals), - put(fd,Fd), - Literals; - Literals -> - Literals - end, + Dict0 = get_literals(Fd,DecodeOpts), Dict = read_heap(Fd,Pid,DecodeOpts,Dict0), Expanded = {read_stack_dump(Fd,Pid,DecodeOpts,Dict), read_messages(Fd,Pid,DecodeOpts,Dict), @@ -1468,6 +1469,18 @@ expand_memory(Fd,Pid,DumpVsn) -> end, {Expanded,IncompleteWarning}. +get_literals(Fd,DecodeOpts) -> + case get(?literals) of + undefined -> + OldFd = put(fd,Fd), + Literals = read_literals(Fd,DecodeOpts), + put(fd,OldFd), + put(?literals,Literals), + Literals; + Literals -> + Literals + end. + read_literals(Fd,DecodeOpts) -> case lookup_index(?literals,[]) of [{_,Start}] -> @@ -1594,31 +1607,92 @@ read_heap(Fd,Pid,DecodeOpts,Dict0) -> Dict0 end. -read_heap(DecodeOpts,Dict0) -> - %% This function is never called if the dump is truncated in {?proc_heap,Pid} - case get(fd) of - end_of_heap -> +read_heap(DecodeOpts, Dict0) -> + %% This function is never called if the dump is truncated in + %% {?proc_heap,Pid}. + %% + %% It is not always possible to reconstruct the heap terms + %% in a single pass, especially if maps are involved. + %% See crashdump_helper:literal_map/0 for an example. + %% + %% Therefore, we need two passes. In the first pass + %% we collect all lines without parsing them, and in the + %% second pass we parse them. + %% + %% The first pass follows. + + Lines0 = read_heap_lines(), + + %% Save a map of all unprocessed lines so that deref_ptr() can + %% access any line when there are references to terms not yet + %% built. + + LineMap = maps:from_list(Lines0), + put(line_map, LineMap), + + %% Refc binaries (tag "Yc") must be processed before any sub + %% binaries (tag "Ys") referencing them, so we make sure to + %% process all the refc binaries first. + %% + %% The other lines can be processed in any order, but processing + %% them in the reverse order compared to how they are printed in + %% the crash dump seems to minimize the number of references to + %% terms that have not yet been built. That happens to be the + %% order of the line list as returned by read_heap_lines/0. + + RefcBins = [Refc || {_,<<"Yc",_/binary>>}=Refc <- Lines0], + Lines = RefcBins ++ Lines0, + + %% Second pass. + + init_progress("Processing terms", map_size(LineMap)), + Dict = parse_heap_terms(Lines, DecodeOpts, Dict0), + erase(line_map), + end_progress(), + Dict. + +read_heap_lines() -> + read_heap_lines_1(get(fd), []). + +read_heap_lines_1(Fd, Acc) -> + case bytes(Fd) of + "=" ++ _next_tag -> 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,DecodeOpts,Dict0), - read_heap(DecodeOpts,Dict) - end + put(fd, end_of_heap), + Acc; + Line0 -> + update_progress(length(Line0)+1), + {Addr,":"++Line1} = get_hex(Line0), + + %% Reduce the memory consumption by converting the + %% line to a binary. Measurements show that it may also + %% be benefical for performance, too, because it makes the + %% garbage collections cheaper. + + Line = list_to_binary(Line1), + read_heap_lines_1(Fd, [{Addr,Line}|Acc]) end. -parse(Line0, DecodeOpts, Dict0) -> - {Addr,":"++Line1} = get_hex(Line0), - {_Term,Line,Dict} = parse_heap_term(Line1, Addr, DecodeOpts, Dict0), - [] = skip_blanks(Line), +parse_heap_terms([{Addr,Line0}|T], DecodeOpts, Dict0) -> + case gb_trees:is_defined(Addr, Dict0) of + true -> + %% Already parsed (by a recursive call from do_deref_ptr() + %% to parse_line()). Nothing to do. + parse_heap_terms(T, DecodeOpts, Dict0); + false -> + %% Parse this previously unparsed term. + Dict = parse_line(Addr, Line0, DecodeOpts, Dict0), + parse_heap_terms(T, DecodeOpts, Dict) + end; +parse_heap_terms([], _DecodeOpts, Dict) -> Dict. +parse_line(Addr, Line0, DecodeOpts, Dict0) -> + update_progress(1), + Line1 = binary_to_list(Line0), + {_Term,Line,Dict} = parse_heap_term(Line1, Addr, DecodeOpts, Dict0), + [] = skip_blanks(Line), %Assertion. + Dict. %%----------------------------------------------------------------- %% Page with one port @@ -2142,6 +2216,56 @@ get_atom(Atom) when is_binary(Atom) -> {Atom,nq}. % not quoted %%----------------------------------------------------------------- +%% Page with list of all persistent terms +persistent_terms(File, DecodeOpts) -> + case lookup_index(?persistent_terms) of + [{_Id,Start}] -> + Fd = open(File), + pos_bof(Fd,Start), + Terms = get_persistent_terms(Fd), + Dict = get_literals(Fd,DecodeOpts), + parse_persistent_terms(Terms,DecodeOpts,Dict); + _ -> + [] + end. + +parse_persistent_terms([[Name0,Val0]|Terms],DecodeOpts,Dict) -> + {Name,_,_} = parse_term(binary_to_list(Name0),DecodeOpts,Dict), + {Val,_,_} = parse_term(binary_to_list(Val0),DecodeOpts,Dict), + [{Name,Val}|parse_persistent_terms(Terms,DecodeOpts,Dict)]; +parse_persistent_terms([],_,_) -> []. + +get_persistent_terms(Fd) -> + case get_chunk(Fd) of + {ok,Bin} -> + get_persistent_terms(Fd,Bin,[]); + eof -> + [] + end. + + +%% Persistent_Terms are written one per line in the crash dump. +get_persistent_terms(Fd,Bin,PersistentTerms) -> + Bins = binary:split(Bin,<<"\n">>,[global]), + get_persistent_terms1(Fd,Bins,PersistentTerms). + +get_persistent_terms1(_Fd,[<<"=",_/binary>>|_],PersistentTerms) -> + PersistentTerms; +get_persistent_terms1(Fd,[LastBin],PersistentTerms) -> + case get_chunk(Fd) of + {ok,Bin0} -> + get_persistent_terms(Fd,<<LastBin/binary,Bin0/binary>>,PersistentTerms); + eof -> + [get_persistent_term(LastBin)|PersistentTerms] + end; +get_persistent_terms1(Fd,[Bin|Bins],Persistent_Terms) -> + get_persistent_terms1(Fd,Bins,[get_persistent_term(Bin)|Persistent_Terms]). + +get_persistent_term(Bin) -> + binary:split(Bin,<<"|">>). + + +%%----------------------------------------------------------------- %% Page with memory information memory(File) -> case lookup_index(?memory) of @@ -2871,16 +2995,18 @@ parse_atom_translation_table(N, Line0, As) -> deref_ptr(Ptr, Line, DecodeOpts, D) -> - Lookup = fun(D0) -> - gb_trees:lookup(Ptr, D0) - end, + Lookup0 = fun(D0) -> + gb_trees:lookup(Ptr, D0) + end, + Lookup = wrap_line_map(Ptr, Lookup0), do_deref_ptr(Lookup, Line, DecodeOpts, D). deref_bin(Binp0, Offset, Sz, Line, DecodeOpts, D) -> Binp = Binp0 bor DecodeOpts#dec_opts.bin_addr_adj, - Lookup = fun(D0) -> - lookup_binary(Binp, Offset, Sz, D0) - end, + Lookup0 = fun(D0) -> + lookup_binary(Binp, Offset, Sz, D0) + end, + Lookup = wrap_line_map(Binp, Lookup0), do_deref_ptr(Lookup, Line, DecodeOpts, D). lookup_binary(Binp, Offset, Sz, D) -> @@ -2899,26 +3025,36 @@ lookup_binary(Binp, Offset, Sz, D) -> end end. +wrap_line_map(Ptr, Lookup) -> + wrap_line_map_1(get(line_map), Ptr, Lookup). + +wrap_line_map_1(#{}=LineMap, Ptr, Lookup) -> + fun(D) -> + case Lookup(D) of + {value,_}=Res -> + Res; + none -> + case LineMap of + #{Ptr:=Line} -> + {line,Ptr,Line}; + #{} -> + none + end + end + end; +wrap_line_map_1(undefined, _Ptr, Lookup) -> + Lookup. + do_deref_ptr(Lookup, Line, DecodeOpts, D0) -> case Lookup(D0) of {value,Term} -> {Term,Line,D0}; none -> - case get(fd) of - end_of_heap -> - put(incomplete_heap,true), - {['#CDVIncompleteHeap'],Line,D0}; - Fd -> - case bytes(Fd) of - "="++_ -> - put(fd, end_of_heap), - do_deref_ptr(Lookup, Line, DecodeOpts, D0); - L -> - update_progress(length(L)+1), - D = parse(L, DecodeOpts, D0), - do_deref_ptr(Lookup, Line, DecodeOpts, D) - end - end + put(incomplete_heap, true), + {['#CDVIncompleteHeap'],Line,D0}; + {line,Addr,NewLine} -> + D = parse_line(Addr, NewLine, DecodeOpts, D0), + do_deref_ptr(Lookup, Line, DecodeOpts, D) end. get_hex(L) -> @@ -3119,6 +3255,7 @@ tag_to_atom("literals") -> ?literals; tag_to_atom("loaded_modules") -> ?loaded_modules; tag_to_atom("memory") -> ?memory; tag_to_atom("mod") -> ?mod; +tag_to_atom("persistent_terms") -> ?persistent_terms; tag_to_atom("no_distribution") -> ?no_distribution; tag_to_atom("node") -> ?node; tag_to_atom("not_connected") -> ?not_connected; |