diff options
Diffstat (limited to 'lib/observer/src/crashdump_viewer.erl')
-rw-r--r-- | lib/observer/src/crashdump_viewer.erl | 558 |
1 files changed, 313 insertions, 245 deletions
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl index 8d70f5e2c8..51ff69fd69 100644 --- a/lib/observer/src/crashdump_viewer.erl +++ b/lib/observer/src/crashdump_viewer.erl @@ -36,7 +36,6 @@ %% file: The name of the crashdump currently viewed. %% dump_vsn: The version number of the crashdump %% wordsize: 4 | 8, the number of bytes in a word. -%% binaries: a gb_tree containing binaries or links to binaries in the dump %% %% User API @@ -87,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 @@ -106,6 +106,8 @@ -define(internal_ets,internal_ets). -define(loaded_modules,loaded_modules). -define(memory,memory). +-define(memory_map,memory_map). +-define(memory_status,memory_status). -define(mod,mod). -define(no_distribution,no_distribution). -define(node,node). @@ -122,7 +124,7 @@ -define(visible_node,visible_node). --record(state,{file,dump_vsn,wordsize=4,num_atoms="unknown",binaries}). +-record(state,{file,dump_vsn,wordsize=4,num_atoms="unknown"}). %%%----------------------------------------------------------------- %%% Debugging @@ -200,13 +202,14 @@ do_script_start(StartFun) -> {'EXIT', Pid, normal} -> ok; {'EXIT', Pid, Reason} -> - io:format("\ncdv crash: ~p\n",[Reason]) + io:format("\ncdv crash: ~tp\n",[Reason]) end; _ -> - io:format("\ncdv crash: ~p\n",[unknown_reason]) + %io:format("\ncdv crash: ~p\n",[unknown_reason]) + ok end; Error -> - io:format("\ncdv start failed: ~p\n",[Error]) + io:format("\ncdv start failed: ~tp\n",[Error]) end. usage() -> @@ -305,6 +308,8 @@ expand_binary(Pos) -> 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{}}. %%-------------------------------------------------------------------- @@ -340,7 +345,7 @@ handle_call(general_info,_From,State=#state{file=File}) -> handle_call({expand_binary,{Offset,Size,Pos}},_From,State=#state{file=File}) -> Fd = open(File), pos_bof(Fd,Pos), - {Bin,_Line} = get_binary(Offset,Size,val(Fd)), + {Bin,_Line} = get_binary(Offset,Size,bytes(Fd)), close(Fd), {reply,{ok,Bin},State}; handle_call(procs_summary,_From,State=#state{file=File,wordsize=WS}) -> @@ -348,9 +353,9 @@ handle_call(procs_summary,_From,State=#state{file=File,wordsize=WS}) -> Procs = procs_summary(File,WS), {reply,{ok,Procs,TW},State}; handle_call({proc_details,Pid},_From, - State=#state{file=File,wordsize=WS,dump_vsn=DumpVsn,binaries=B})-> + State=#state{file=File,wordsize=WS,dump_vsn=DumpVsn})-> Reply = - case get_proc_details(File,Pid,WS,DumpVsn,B) of + case get_proc_details(File,Pid,WS,DumpVsn) of {ok,Proc,TW} -> {ok,Proc,TW}; Other -> @@ -462,9 +467,9 @@ handle_call(schedulers,_From,State=#state{file=File}) -> %%-------------------------------------------------------------------- handle_cast({read_file,File}, _State) -> case do_read_file(File) of - {ok,Binaries,DumpVsn} -> + {ok,DumpVsn} -> observer_lib:report_progress({ok,done}), - {noreply, #state{file=File,binaries=Binaries,dump_vsn=DumpVsn}}; + {noreply, #state{file=File,dump_vsn=DumpVsn}}; Error -> end_progress(Error), {noreply, #state{}} @@ -512,9 +517,9 @@ unexpected(_Fd,{eof,_LastLine},_Where) -> ok; % truncated file unexpected(Fd,{part,What},Where) -> skip_rest_of_line(Fd), - io:format("WARNING: Found unexpected line in ~s:~n~s ...~n",[Where,What]); + io:format("WARNING: Found unexpected line in ~ts:~n~ts ...~n",[Where,What]); unexpected(_Fd,What,Where) -> - io:format("WARNING: Found unexpected line in ~s:~n~s~n",[Where,What]). + io:format("WARNING: Found unexpected line in ~ts:~n~ts~n",[Where,What]). truncated_warning([]) -> []; @@ -701,9 +706,24 @@ skip(Fd,<<>>) -> end. -val(Fd) -> - val(Fd, "-1"). -val(Fd, NoExist) -> +string(Fd) -> + string(Fd, "-1"). +string(Fd,NoExist) -> + case bytes(Fd,noexist) of + noexist -> NoExist; + Val -> byte_list_to_string(Val) + end. + +byte_list_to_string(ByteList) -> + Bin = list_to_binary(ByteList), + case unicode:characters_to_list(Bin) of + Str when is_list(Str) -> Str; + _ -> ByteList + end. + +bytes(Fd) -> + bytes(Fd, "-1"). +bytes(Fd, NoExist) -> case get_rest_of_line(Fd) of {eof,[]} -> NoExist; [] -> NoExist; @@ -731,32 +751,6 @@ get_rest_of_line_1(Fd, <<>>, Acc) -> eof -> {eof,lists:reverse(Acc)} end. -get_lines_to_empty(Fd) -> - case get_chunk(Fd) of - {ok,Bin} -> - get_lines_to_empty(Fd,Bin,[],[]); - eof -> - [] - end. -get_lines_to_empty(Fd,<<$\n:8,Bin/binary>>,[],Lines) -> - put_chunk(Fd,Bin), - lists:reverse(Lines); -get_lines_to_empty(Fd,<<$\n:8,Bin/binary>>,Acc,Lines) -> - get_lines_to_empty(Fd,Bin,[],[lists:reverse(Acc)|Lines]); -get_lines_to_empty(Fd,<<$\r:8,Bin/binary>>,Acc,Lines) -> - get_lines_to_empty(Fd,Bin,Acc,Lines); -get_lines_to_empty(Fd,<<$\s:8,Bin/binary>>,[],Lines) -> - get_lines_to_empty(Fd,Bin,[],Lines); -get_lines_to_empty(Fd,<<Char:8,Bin/binary>>,Acc,Lines) -> - get_lines_to_empty(Fd,Bin,[Char|Acc],Lines); -get_lines_to_empty(Fd,<<>>,Acc,Lines) -> - case get_chunk(Fd) of - {ok,Bin} -> - get_lines_to_empty(Fd,Bin,Acc,Lines); - eof -> - lists:reverse(Lines,[lists:reverse(Acc)]) - end. - split(Str) -> split($ ,Str,[]). split(Char,Str) -> @@ -802,21 +796,20 @@ do_read_file(File) -> {Tag,Id,Rest,N1} = tag(Fd,TagAndRest,1), case Tag of ?erl_crash_dump -> - reset_index_table(), + reset_tables(), insert_index(Tag,Id,N1+1), put_last_tag(Tag,""), - indexify(Fd,Rest,N1), + DumpVsn = [list_to_integer(L) || + L<-string:lexemes(Id,".")], + AddrAdj = get_bin_addr_adj(DumpVsn), + indexify(Fd,AddrAdj,Rest,N1), end_progress(), check_if_truncated(), - [{DumpVsn0,_}] = lookup_index(?erl_crash_dump), - DumpVsn = [list_to_integer(L) || - L<-string:tokens(DumpVsn0,".")], - Binaries = read_binaries(Fd,DumpVsn), close(Fd), - {ok,Binaries,DumpVsn}; + {ok,DumpVsn}; _Other -> R = io_lib:format( - "~s is not an Erlang crash dump~n", + "~ts is not an Erlang crash dump~n", [File]), close(Fd), {error,R} @@ -824,32 +817,48 @@ do_read_file(File) -> {ok,<<"<Erlang crash dump>",_Rest/binary>>} -> %% old version - no longer supported R = io_lib:format( - "The crashdump ~s is in the pre-R10B format, " + "The crashdump ~ts is in the pre-R10B format, " "which is no longer supported.~n", [File]), close(Fd), {error,R}; _Other -> R = io_lib:format( - "~s is not an Erlang crash dump~n", + "~ts is not an Erlang crash dump~n", [File]), close(Fd), {error,R} end; _other -> - R = io_lib:format("~s is not an Erlang crash dump~n",[File]), + R = io_lib:format("~ts is not an Erlang crash dump~n",[File]), {error,R} end. -indexify(Fd,Bin,N) -> +indexify(Fd,AddrAdj,Bin,N) -> case binary:match(Bin,<<"\n=">>) of {Start,Len} -> Pos = Start+Len, <<_:Pos/binary,TagAndRest/binary>> = Bin, {Tag,Id,Rest,N1} = tag(Fd,TagAndRest,N+Pos), - insert_index(Tag,Id,N1+1), % +1 to get past newline - put_last_tag(Tag,Id), - indexify(Fd,Rest,N1); + NewPos = N1+1, % +1 to get past newline + case Tag of + ?binary -> + %% Binaries are stored in a separate table in + %% order to minimize lookup time. Key is the + %% translated address. + {HexAddr,_} = get_hex(Id), + Addr = HexAddr bor AddrAdj, + insert_binary_index(Addr,NewPos); + _ -> + insert_index(Tag,Id,NewPos) + end, + 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 {ok,Chunk0} when is_binary(Chunk0) -> @@ -860,7 +869,7 @@ indexify(Fd,Bin,N) -> _ -> {Chunk0,N+byte_size(Bin)} end, - indexify(Fd,Chunk,N1); + indexify(Fd,AddrAdj,Chunk,N1); eof -> eof end @@ -896,7 +905,11 @@ check_if_truncated() -> find_truncated_proc(TruncatedTag) end. -find_truncated_proc({?atoms,_Id}) -> +find_truncated_proc({Tag,_Id}) when Tag==?atoms; + Tag==?binary; + Tag==?instr_data; + Tag==?memory_status; + Tag==?memory_map -> put(truncated_proc,false); find_truncated_proc({Tag,Pid}) -> case is_proc_tag(Tag) of @@ -986,7 +999,7 @@ general_info(File) -> instr_info=InstrInfo}. get_slogan_and_sysvsn(Fd,Acc) -> - case val(Fd,eof) of + case string(Fd,eof) of "Slogan: " ++ SloganPart when Acc==[] -> get_slogan_and_sysvsn(Fd,[SloganPart]); "System version: " ++ SystemVsn -> @@ -1000,14 +1013,14 @@ get_slogan_and_sysvsn(Fd,Acc) -> get_general_info(Fd,GenInfo) -> case line_head(Fd) of "Compiled" -> - get_general_info(Fd,GenInfo#general_info{compile_time=val(Fd)}); + get_general_info(Fd,GenInfo#general_info{compile_time=bytes(Fd)}); "Taints" -> - Val = case val(Fd) of "-1" -> "(none)"; Line -> Line end, + Val = case string(Fd) of "-1" -> "(none)"; Line -> Line end, get_general_info(Fd,GenInfo#general_info{taints=Val}); "Atoms" -> - get_general_info(Fd,GenInfo#general_info{num_atoms=val(Fd)}); + get_general_info(Fd,GenInfo#general_info{num_atoms=bytes(Fd)}); "Calling Thread" -> - get_general_info(Fd,GenInfo#general_info{thread=val(Fd)}); + get_general_info(Fd,GenInfo#general_info{thread=bytes(Fd)}); "=" ++ _next_tag -> GenInfo; Other -> @@ -1045,14 +1058,14 @@ procs_summary(File,WS) -> %%----------------------------------------------------------------- %% Page with one process -get_proc_details(File,Pid,WS,DumpVsn,Binaries) -> +get_proc_details(File,Pid,WS,DumpVsn) -> case lookup_index(?proc,Pid) of [{_,Start}] -> Fd = open(File), {{Stack,MsgQ,Dict},TW} = case truncated_warning([{?proc,Pid}]) of [] -> - {expand_memory(Fd,Pid,DumpVsn,Binaries),[]}; + {expand_memory(Fd,Pid,DumpVsn),[]}; TW0 -> {{[],[],[]},TW0} end, @@ -1068,15 +1081,15 @@ get_proc_details(File,Pid,WS,DumpVsn,Binaries) -> get_procinfo(Fd,Fun,Proc,WS) -> case line_head(Fd) of "State" -> - State = case val(Fd) of + State = case bytes(Fd) of "Garbing" -> "Garbing\n(limited info)"; State0 -> State0 end, get_procinfo(Fd,Fun,Proc#proc{state=State},WS); "Name" -> - get_procinfo(Fd,Fun,Proc#proc{name=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{name=string(Fd)},WS); "Spawned as" -> - IF = val(Fd), + IF = string(Fd), case Proc#proc.name of undefined -> get_procinfo(Fd,Fun,Proc#proc{name=IF,init_func=IF},WS); @@ -1085,17 +1098,17 @@ get_procinfo(Fd,Fun,Proc,WS) -> end; "Message queue length" -> %% stored as integer so we can sort on it - get_procinfo(Fd,Fun,Proc#proc{msg_q_len=list_to_integer(val(Fd))},WS); + get_procinfo(Fd,Fun,Proc#proc{msg_q_len=list_to_integer(bytes(Fd))},WS); "Reductions" -> %% stored as integer so we can sort on it - get_procinfo(Fd,Fun,Proc#proc{reds=list_to_integer(val(Fd))},WS); + get_procinfo(Fd,Fun,Proc#proc{reds=list_to_integer(bytes(Fd))},WS); "Stack+heap" -> %% stored as integer so we can sort on it get_procinfo(Fd,Fun,Proc#proc{stack_heap= - list_to_integer(val(Fd))*WS},WS); + list_to_integer(bytes(Fd))*WS},WS); "Memory" -> %% stored as integer so we can sort on it - get_procinfo(Fd,Fun,Proc#proc{memory=list_to_integer(val(Fd))},WS); + get_procinfo(Fd,Fun,Proc#proc{memory=list_to_integer(bytes(Fd))},WS); {eof,_} -> Proc; % truncated file Other -> @@ -1117,67 +1130,67 @@ all_procinfo(Fd,Fun,Proc,WS,LineHead) -> case LineHead of %% - START - moved from get_procinfo - "Spawned by" -> - case val(Fd) of + case bytes(Fd) of "[]" -> get_procinfo(Fd,Fun,Proc,WS); Parent -> get_procinfo(Fd,Fun,Proc#proc{parent=Parent},WS) end; "Started" -> - get_procinfo(Fd,Fun,Proc#proc{start_time=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{start_time=bytes(Fd)},WS); "Last scheduled in for" -> get_procinfo(Fd,Fun,Proc#proc{current_func= {"Last scheduled in for", - val(Fd)}},WS); + string(Fd)}},WS); "Current call" -> get_procinfo(Fd,Fun,Proc#proc{current_func={"Current call", - val(Fd)}},WS); + string(Fd)}},WS); "Number of heap fragments" -> - get_procinfo(Fd,Fun,Proc#proc{num_heap_frag=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{num_heap_frag=bytes(Fd)},WS); "Heap fragment data" -> - get_procinfo(Fd,Fun,Proc#proc{heap_frag_data=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{heap_frag_data=bytes(Fd)},WS); "OldHeap" -> - Bytes = list_to_integer(val(Fd))*WS, + Bytes = list_to_integer(bytes(Fd))*WS, get_procinfo(Fd,Fun,Proc#proc{old_heap=Bytes},WS); "Heap unused" -> - Bytes = list_to_integer(val(Fd))*WS, + Bytes = list_to_integer(bytes(Fd))*WS, get_procinfo(Fd,Fun,Proc#proc{heap_unused=Bytes},WS); "OldHeap unused" -> - Bytes = list_to_integer(val(Fd))*WS, + Bytes = list_to_integer(bytes(Fd))*WS, get_procinfo(Fd,Fun,Proc#proc{old_heap_unused=Bytes},WS); "New heap start" -> - get_procinfo(Fd,Fun,Proc#proc{new_heap_start=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{new_heap_start=bytes(Fd)},WS); "New heap top" -> - get_procinfo(Fd,Fun,Proc#proc{new_heap_top=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{new_heap_top=bytes(Fd)},WS); "Stack top" -> - get_procinfo(Fd,Fun,Proc#proc{stack_top=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{stack_top=bytes(Fd)},WS); "Stack end" -> - get_procinfo(Fd,Fun,Proc#proc{stack_end=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{stack_end=bytes(Fd)},WS); "Old heap start" -> - get_procinfo(Fd,Fun,Proc#proc{old_heap_start=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{old_heap_start=bytes(Fd)},WS); "Old heap top" -> - get_procinfo(Fd,Fun,Proc#proc{old_heap_top=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{old_heap_top=bytes(Fd)},WS); "Old heap end" -> - get_procinfo(Fd,Fun,Proc#proc{old_heap_end=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{old_heap_end=bytes(Fd)},WS); %% - END - moved from get_procinfo - "Last calls" -> - get_procinfo(Fd,Fun,Proc#proc{last_calls=get_lines_to_empty(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{last_calls=get_last_calls(Fd)},WS); "Link list" -> - {Links,Monitors,MonitoredBy} = parse_link_list(val(Fd),[],[],[]), + {Links,Monitors,MonitoredBy} = parse_link_list(bytes(Fd),[],[],[]), get_procinfo(Fd,Fun,Proc#proc{links=Links, monitors=Monitors, mon_by=MonitoredBy},WS); "Program counter" -> - get_procinfo(Fd,Fun,Proc#proc{prog_count=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{prog_count=string(Fd)},WS); "CP" -> - get_procinfo(Fd,Fun,Proc#proc{cp=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{cp=string(Fd)},WS); "arity = " ++ Arity -> %%! Temporary workaround get_procinfo(Fd,Fun,Proc#proc{arity=Arity--"\r\n"},WS); "Run queue" -> - get_procinfo(Fd,Fun,Proc#proc{run_queue=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{run_queue=string(Fd)},WS); "Internal State" -> - get_procinfo(Fd,Fun,Proc#proc{int_state=val(Fd)},WS); + get_procinfo(Fd,Fun,Proc#proc{int_state=string(Fd)},WS); "=" ++ _next_tag -> Proc; Other -> @@ -1185,6 +1198,66 @@ all_procinfo(Fd,Fun,Proc,WS,LineHead) -> get_procinfo(Fd,Fun,Proc,WS) end. +%% The end of the 'Last calls' section is meant to be an empty line, +%% but in some cases this is not the case, so we also need to look for +%% the next heading which currently (OTP-20.1) can be "Link list: ", +%% "Dictionary: " or "Reductions: ". We do this by looking for ": " +%% and when found, pushing the heading back into the saved chunk. +%% +%% Note that the 'Last calls' section is only present if the +%% 'save_calls' process flag is set. +get_last_calls(Fd) -> + case get_chunk(Fd) of + {ok,Bin} -> + get_last_calls(Fd,Bin,[],[]); + eof -> + [] + end. +get_last_calls(Fd,<<$\n:8,Bin/binary>>,[],Lines) -> + %% Empty line - we're done + put_chunk(Fd,Bin), + lists:reverse(Lines); +get_last_calls(Fd,<<$::8>>,Acc,Lines) -> + case get_chunk(Fd) of + {ok,Bin} -> + %% Could be a colon followed by a space - see next function clause + get_last_calls(Fd,<<$::8,Bin/binary>>,Acc,Lines); + eof -> + %% Truncated here - either we've got the next heading, or + %% it was truncated in a last call function, in which case + %% we note that it was truncated + case byte_list_to_string(lists:reverse(Acc)) of + NextHeading when NextHeading=="Link list"; + NextHeading=="Dictionary"; + NextHeading=="Reductions" -> + put_chunk(Fd,list_to_binary(NextHeading++":")), + lists:reverse(Lines); + LastCallFunction-> + lists:reverse(Lines,[LastCallFunction++":...(truncated)"]) + end + end; +get_last_calls(Fd,<<$\::8,$\s:8,Bin/binary>>,Acc,Lines) -> + %% ": " - means we have the next heading in Acc - save it back + %% into the chunk and return the lines we've found + HeadingBin = list_to_binary(lists:reverse(Acc,[$:])), + put_chunk(Fd,<<HeadingBin/binary,Bin/binary>>), + lists:reverse(Lines); +get_last_calls(Fd,<<$\n:8,Bin/binary>>,Acc,Lines) -> + get_last_calls(Fd,Bin,[],[byte_list_to_string(lists:reverse(Acc))|Lines]); +get_last_calls(Fd,<<$\r:8,Bin/binary>>,Acc,Lines) -> + get_last_calls(Fd,Bin,Acc,Lines); +get_last_calls(Fd,<<$\s:8,Bin/binary>>,[],Lines) -> + get_last_calls(Fd,Bin,[],Lines); +get_last_calls(Fd,<<Char:8,Bin/binary>>,Acc,Lines) -> + get_last_calls(Fd,Bin,[Char|Acc],Lines); +get_last_calls(Fd,<<>>,Acc,Lines) -> + case get_chunk(Fd) of + {ok,Bin} -> + get_last_calls(Fd,Bin,Acc,Lines); + eof -> + lists:reverse(Lines,[byte_list_to_string(lists:reverse(Acc))]) + end. + parse_link_list([SB|Str],Links,Monitors,MonitoredBy) when SB==$[; SB==$] -> parse_link_list(Str,Links,Monitors,MonitoredBy); parse_link_list("#Port"++_=Str,Links,Monitors,MonitoredBy) -> @@ -1204,7 +1277,7 @@ parse_link_list(", "++Rest,Links,Monitors,MonitoredBy) -> parse_link_list([],Links,Monitors,MonitoredBy) -> {lists:reverse(Links),lists:reverse(Monitors),lists:reverse(MonitoredBy)}; parse_link_list(Unexpected,Links,Monitors,MonitoredBy) -> - io:format("WARNING: found unexpected data in link list:~n~s~n",[Unexpected]), + io:format("WARNING: found unexpected data in link list:~n~ts~n",[Unexpected]), parse_link_list([],Links,Monitors,MonitoredBy). @@ -1220,12 +1293,12 @@ parse_monitor("{"++Str) -> %% Named process {Name,Node,Rest1} = parse_name_node(Str,[]), Pid = get_pid_from_name(Name,Node), - case parse_link(string:strip(Rest1,left,$,),[]) of + case parse_link(string:trim(Rest1,leading,","),[]) of {Ref,"}"++Rest2} -> %% Bug in break.c - prints an extra "}" for remote %% nodes... thus the strip {{Pid,"{"++Name++","++Node++"} ("++Ref++")"}, - string:strip(Rest2,left,$})}; + string:trim(Rest2,leading,"}")}; {Ref,[]} -> {{Pid,"{"++Name++","++Node++"} ("++Ref++")"},[]} end; @@ -1310,10 +1383,10 @@ maybe_other_node2(Channel) -> end. -expand_memory(Fd,Pid,DumpVsn,Binaries) -> +expand_memory(Fd,Pid,DumpVsn) -> BinAddrAdj = get_bin_addr_adj(DumpVsn), put(fd,Fd), - Dict = read_heap(Fd,Pid,BinAddrAdj,Binaries), + Dict = read_heap(Fd,Pid,BinAddrAdj,gb_trees:empty()), Expanded = {read_stack_dump(Fd,Pid,BinAddrAdj,Dict), read_messages(Fd,Pid,BinAddrAdj,Dict), read_dictionary(Fd,Pid,BinAddrAdj,Dict)}, @@ -1331,25 +1404,6 @@ get_bin_addr_adj(_) -> 0. %%% -%%% Read binaries. -%%% -read_binaries(Fd,DumpVsn) -> - AllBinaries = lookup_index(?binary), - AddrAdj = get_bin_addr_adj(DumpVsn), - Fun = fun({Addr0,Pos},Dict0) -> - pos_bof(Fd,Pos), - {HexAddr,_} = get_hex(Addr0), - Addr = HexAddr bor AddrAdj, - Bin = - case line_head(Fd) of - {eof,_} -> '#CDVTruncatedBinary'; - _Size -> {'#CDVBin',Pos} - end, - gb_trees:enter(Addr,Bin,Dict0) - end, - progress_foldl("Processing binaries",Fun,gb_trees:empty(),AllBinaries). - -%%% %%% Read top level section. %%% @@ -1363,7 +1417,7 @@ read_stack_dump(Fd,Pid,BinAddrAdj,Dict) -> end. read_stack_dump1(Fd,BinAddrAdj,Dict,Acc) -> %% This function is never called if the dump is truncated in {?proc_heap,Pid} - case val(Fd) of + case bytes(Fd) of "=" ++ _next_tag -> lists:reverse(Acc); Line -> @@ -1391,7 +1445,7 @@ read_messages(Fd,Pid,BinAddrAdj,Dict) -> end. read_messages1(Fd,BinAddrAdj,Dict,Acc) -> %% This function is never called if the dump is truncated in {?proc_heap,Pid} - case val(Fd) of + case bytes(Fd) of "=" ++ _next_tag -> lists:reverse(Acc); Line -> @@ -1419,7 +1473,7 @@ read_dictionary(Fd,Pid,BinAddrAdj,Dict) -> end. read_dictionary1(Fd,BinAddrAdj,Dict,Acc) -> %% This function is never called if the dump is truncated in {?proc_heap,Pid} - case val(Fd) of + case bytes(Fd) of "=" ++ _next_tag -> lists:reverse(Acc); Line -> @@ -1439,6 +1493,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); [] -> @@ -1449,13 +1505,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 val(Fd) of + 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 @@ -1491,49 +1550,49 @@ get_ports(File) -> %% Converting port string to tuple to secure correct sorting. This is %% converted back in cdv_port_cb:format/1. port_to_tuple("#Port<"++Port) -> - [I1,I2] = string:tokens(Port,".>"), + [I1,I2] = string:lexemes(Port,".>"), {list_to_integer(I1),list_to_integer(I2)}. get_portinfo(Fd,Port) -> case line_head(Fd) of "Slot" -> %% stored as integer so we can sort on it - get_portinfo(Fd,Port#port{slot=list_to_integer(val(Fd))}); + get_portinfo(Fd,Port#port{slot=list_to_integer(bytes(Fd))}); "Connected" -> %% stored as pid so we can sort on it - Connected0 = val(Fd), + Connected0 = bytes(Fd), Connected = try list_to_pid(Connected0) catch error:badarg -> Connected0 end, get_portinfo(Fd,Port#port{connected=Connected}); "Links" -> - Pids = split_pid_list_no_space(val(Fd)), + Pids = split_pid_list_no_space(bytes(Fd)), Links = [{Pid,Pid} || Pid <- Pids], get_portinfo(Fd,Port#port{links=Links}); "Registered as" -> - get_portinfo(Fd,Port#port{name=val(Fd)}); + get_portinfo(Fd,Port#port{name=string(Fd)}); "Monitors" -> - Monitors0 = string:tokens(val(Fd),"()"), + Monitors0 = string:lexemes(bytes(Fd),"()"), Monitors = [begin - [Pid,Ref] = string:tokens(Mon,","), + [Pid,Ref] = string:lexemes(Mon,","), {Pid,Pid++" ("++Ref++")"} end || Mon <- Monitors0], get_portinfo(Fd,Port#port{monitors=Monitors}); "Port controls linked-in driver" -> - Str = lists:flatten(["Linked in driver: " | val(Fd)]), + Str = lists:flatten(["Linked in driver: " | string(Fd)]), get_portinfo(Fd,Port#port{controls=Str}); "Port controls forker process" -> - Str = lists:flatten(["Forker process: " | val(Fd)]), + Str = lists:flatten(["Forker process: " | string(Fd)]), get_portinfo(Fd,Port#port{controls=Str}); "Port controls external process" -> - Str = lists:flatten(["External proc: " | val(Fd)]), + Str = lists:flatten(["External proc: " | string(Fd)]), get_portinfo(Fd,Port#port{controls=Str}); "Port is a file" -> - Str = lists:flatten(["File: "| val(Fd)]), + Str = lists:flatten(["File: "| string(Fd)]), get_portinfo(Fd,Port#port{controls=Str}); "Port is UNIX fd not opened by emulator" -> - Str = lists:flatten(["UNIX fd not opened by emulator: "| val(Fd)]), + Str = lists:flatten(["UNIX fd not opened by emulator: "| string(Fd)]), get_portinfo(Fd,Port#port{controls=Str}); "=" ++ _next_tag -> Port; @@ -1566,23 +1625,23 @@ tab_is_named(#ets_table{}) -> "no". get_etsinfo(Fd,EtsTable = #ets_table{details=Ds},WS) -> case line_head(Fd) of "Slot" -> - get_etsinfo(Fd,EtsTable#ets_table{slot=list_to_integer(val(Fd))},WS); + get_etsinfo(Fd,EtsTable#ets_table{slot=list_to_integer(bytes(Fd))},WS); "Table" -> - get_etsinfo(Fd,EtsTable#ets_table{id=val(Fd)},WS); + get_etsinfo(Fd,EtsTable#ets_table{id=string(Fd)},WS); "Name" -> - get_etsinfo(Fd,EtsTable#ets_table{name=val(Fd)},WS); + get_etsinfo(Fd,EtsTable#ets_table{name=string(Fd)},WS); "Ordered set (AVL tree), Elements" -> skip_rest_of_line(Fd), get_etsinfo(Fd,EtsTable#ets_table{data_type="tree"},WS); "Buckets" -> %% A bug in erl_db_hash.c prints a space after the buckets %% - need to strip the string to make list_to_integer/1 happy. - Buckets = list_to_integer(string:strip(val(Fd))), + Buckets = list_to_integer(string:trim(bytes(Fd),both,"\s")), get_etsinfo(Fd,EtsTable#ets_table{buckets=Buckets},WS); "Objects" -> - get_etsinfo(Fd,EtsTable#ets_table{size=list_to_integer(val(Fd))},WS); + get_etsinfo(Fd,EtsTable#ets_table{size=list_to_integer(bytes(Fd))},WS); "Words" -> - Words = list_to_integer(val(Fd)), + Words = list_to_integer(bytes(Fd)), Bytes = case Words of -1 -> -1; % probably truncated @@ -1592,37 +1651,39 @@ get_etsinfo(Fd,EtsTable = #ets_table{details=Ds},WS) -> "=" ++ _next_tag -> EtsTable; "Chain Length Min" -> - Val = val(Fd), + Val = bytes(Fd), get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_min=>Val}},WS); "Chain Length Avg" -> - Val = try list_to_float(string:strip(val(Fd))) catch _:_ -> "-" end, + Val = try list_to_float(string:trim(bytes(Fd),both,"\s")) + catch _:_ -> "-" + end, get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_avg=>Val}},WS); "Chain Length Max" -> - Val = val(Fd), + Val = bytes(Fd), get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_max=>Val}},WS); "Chain Length Std Dev" -> - Val = val(Fd), + Val = bytes(Fd), get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_stddev=>Val}},WS); "Chain Length Expected Std Dev" -> - Val = val(Fd), + Val = bytes(Fd), get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_exp_stddev=>Val}},WS); "Fixed" -> - Val = val(Fd), + Val = bytes(Fd), get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{fixed=>Val}},WS); "Type" -> - Val = val(Fd), + Val = bytes(Fd), get_etsinfo(Fd,EtsTable#ets_table{data_type=Val},WS); "Protection" -> - Val = val(Fd), + Val = bytes(Fd), get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{protection=>Val}},WS); "Compressed" -> - Val = val(Fd), + Val = bytes(Fd), get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{compressed=>Val}},WS); "Write Concurrency" -> - Val = val(Fd), + Val = bytes(Fd), get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{write_c=>Val}},WS); "Read Concurrency" -> - Val = val(Fd), + Val = bytes(Fd), get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{read_c=>Val}},WS); Other -> unexpected(Fd,Other,"ETS info"), @@ -1672,9 +1733,9 @@ get_timerinfo(Fd,Id) -> get_timerinfo_1(Fd,Timer) -> case line_head(Fd) of "Message" -> - get_timerinfo_1(Fd,Timer#timer{msg=val(Fd)}); + get_timerinfo_1(Fd,Timer#timer{msg=string(Fd)}); "Time left" -> - TimeLeft = list_to_integer(val(Fd) -- " ms"), + TimeLeft = list_to_integer(bytes(Fd) -- " ms"), get_timerinfo_1(Fd,Timer#timer{time=TimeLeft}); "=" ++ _next_tag -> Timer; @@ -1743,37 +1804,37 @@ get_nodeinfo(Fd,Channel,Type,Start) -> get_nodeinfo(Fd,Nod) -> case line_head(Fd) of "Name" -> - get_nodeinfo(Fd,Nod#nod{name=val(Fd)}); + get_nodeinfo(Fd,Nod#nod{name=bytes(Fd)}); "Controller" -> - get_nodeinfo(Fd,Nod#nod{controller=val(Fd)}); + get_nodeinfo(Fd,Nod#nod{controller=bytes(Fd)}); "Creation" -> %% Throwing away elements like "(refc=1)", which might be %% printed from a debug compiled emulator. Creations = lists:flatmap(fun(C) -> try [list_to_integer(C)] catch error:badarg -> [] end - end, string:tokens(val(Fd)," ")), + end, string:lexemes(bytes(Fd)," ")), get_nodeinfo(Fd,Nod#nod{creation={creations,Creations}}); "Remote link" -> - Procs = val(Fd), % e.g. "<0.31.0> <4322.54.0>" + Procs = bytes(Fd), % e.g. "<0.31.0> <4322.54.0>" {Local,Remote} = split(Procs), Str = Local++" <-> "++Remote, NewRemLinks = [{Local,Str} | Nod#nod.remote_links], get_nodeinfo(Fd,Nod#nod{remote_links=NewRemLinks}); "Remote monitoring" -> - Procs = val(Fd), % e.g. "<0.31.0> <4322.54.0>" + Procs = bytes(Fd), % e.g. "<0.31.0> <4322.54.0>" {Local,Remote} = split(Procs), Str = Local++" -> "++Remote, NewRemMon = [{Local,Str} | Nod#nod.remote_mon], get_nodeinfo(Fd,Nod#nod{remote_mon=NewRemMon}); "Remotely monitored by" -> - Procs = val(Fd), % e.g. "<0.31.0> <4322.54.0>" + Procs = bytes(Fd), % e.g. "<0.31.0> <4322.54.0>" {Local,Remote} = split(Procs), Str = Local++" <- "++Remote, NewRemMonBy = [{Local,Str} | Nod#nod.remote_mon_by], get_nodeinfo(Fd,Nod#nod{remote_mon_by=NewRemMonBy}); "Error" -> - get_nodeinfo(Fd,Nod#nod{error="ERROR: "++val(Fd)}); + get_nodeinfo(Fd,Nod#nod{error="ERROR: "++string(Fd)}); "=" ++ _next_tag -> Nod; Other -> @@ -1817,9 +1878,9 @@ loaded_mods(File) -> get_loaded_mod_totals(Fd,{CC,OC}) -> case line_head(Fd) of "Current code" -> - get_loaded_mod_totals(Fd,{val(Fd),OC}); + get_loaded_mod_totals(Fd,{bytes(Fd),OC}); "Old code" -> - get_loaded_mod_totals(Fd,{CC,val(Fd)}); + get_loaded_mod_totals(Fd,{CC,bytes(Fd)}); "=" ++ _next_tag -> {CC,OC}; Other -> @@ -1830,10 +1891,10 @@ get_loaded_mod_totals(Fd,{CC,OC}) -> get_loaded_mod_info(Fd,LM,Fun) -> case line_head(Fd) of "Current size" -> - CS = list_to_integer(val(Fd)), + CS = list_to_integer(bytes(Fd)), get_loaded_mod_info(Fd,LM#loaded_mod{current_size=CS},Fun); "Old size" -> - OS = list_to_integer(val(Fd)), + OS = list_to_integer(bytes(Fd)), get_loaded_mod_info(Fd,LM#loaded_mod{old_size=OS},Fun); "=" ++ _next_tag -> LM; @@ -1849,16 +1910,16 @@ main_modinfo(_Fd,LM,_LineHead) -> all_modinfo(Fd,LM,LineHead) -> case LineHead of "Current attributes" -> - Str = hex_to_str(val(Fd,"")), + Str = hex_to_str(bytes(Fd,"")), LM#loaded_mod{current_attrib=Str}; "Current compilation info" -> - Str = hex_to_str(val(Fd,"")), + Str = hex_to_str(bytes(Fd,"")), LM#loaded_mod{current_comp_info=Str}; "Old attributes" -> - Str = hex_to_str(val(Fd,"")), + Str = hex_to_str(bytes(Fd,"")), LM#loaded_mod{old_attrib=Str}; "Old compilation info" -> - Str = hex_to_str(val(Fd,"")), + Str = hex_to_str(bytes(Fd,"")), LM#loaded_mod{old_comp_info=Str}; Other -> unexpected(Fd,Other,"loaded modules info"), @@ -1868,7 +1929,7 @@ all_modinfo(Fd,LM,LineHead) -> hex_to_str(Hex) -> Term = hex_to_term(Hex,[]), - io_lib:format("~p~n",[Term]). + io_lib:format("~tp~n",[Term]). hex_to_term([X,Y|Hex],Acc) -> MS = hex_to_dec([X]), @@ -1909,17 +1970,17 @@ funs(File) -> get_funinfo(Fd,Fu) -> case line_head(Fd) of "Module" -> - get_funinfo(Fd,Fu#fu{module=val(Fd)}); + get_funinfo(Fd,Fu#fu{module=bytes(Fd)}); "Uniq" -> - get_funinfo(Fd,Fu#fu{uniq=list_to_integer(val(Fd))}); + get_funinfo(Fd,Fu#fu{uniq=list_to_integer(bytes(Fd))}); "Index" -> - get_funinfo(Fd,Fu#fu{index=list_to_integer(val(Fd))}); + get_funinfo(Fd,Fu#fu{index=list_to_integer(bytes(Fd))}); "Address" -> - get_funinfo(Fd,Fu#fu{address=val(Fd)}); + get_funinfo(Fd,Fu#fu{address=bytes(Fd)}); "Native_address" -> - get_funinfo(Fd,Fu#fu{native_address=val(Fd)}); + get_funinfo(Fd,Fu#fu{native_address=bytes(Fd)}); "Refc" -> - get_funinfo(Fd,Fu#fu{refc=list_to_integer(val(Fd))}); + get_funinfo(Fd,Fu#fu{refc=list_to_integer(bytes(Fd))}); "=" ++ _next_tag -> Fu; Other -> @@ -1999,7 +2060,7 @@ get_meminfo(Fd,Acc) -> {eof,_last_line} -> lists:reverse(Acc); Key -> - get_meminfo(Fd,[{list_to_atom(Key),val(Fd)}|Acc]) + get_meminfo(Fd,[{list_to_atom(Key),bytes(Fd)}|Acc]) end. %%----------------------------------------------------------------- @@ -2023,7 +2084,7 @@ get_allocareainfo(Fd,Acc) -> {eof,_last_line} -> lists:reverse(Acc); Key -> - Val = val(Fd), + Val = bytes(Fd), AllocInfo = case split(Val) of {Alloc,[]} -> @@ -2061,7 +2122,7 @@ get_allocatorinfo1(Fd,Acc,Max) -> {eof,_last_line} -> pad_and_reverse(Acc,Max,[]); Key -> - Values = get_all_vals(val(Fd),[]), + Values = get_all_vals(bytes(Fd),[]), L = length(Values), Max1 = if L > Max -> L; true -> Max end, get_allocatorinfo1(Fd,[{Key,Values}|Acc],Max1) @@ -2198,7 +2259,7 @@ get_size_value(Key,Data) -> %% and Value is the sum over all allocator instances of each type. sort_allocator_types([{Name,Data}|Allocators],Acc,DoTotal) -> Type = - case string:tokens(Name,"[]") of + case string:lexemes(Name,"[]") of [T,_Id] -> T; [Name] -> Name; Other -> Other @@ -2316,13 +2377,13 @@ get_hashtableinfo(Fd,Name,Start) -> get_hashtableinfo1(Fd,HashTable) -> case line_head(Fd) of "size" -> - get_hashtableinfo1(Fd,HashTable#hash_table{size=val(Fd)}); + get_hashtableinfo1(Fd,HashTable#hash_table{size=bytes(Fd)}); "used" -> - get_hashtableinfo1(Fd,HashTable#hash_table{used=val(Fd)}); + get_hashtableinfo1(Fd,HashTable#hash_table{used=bytes(Fd)}); "objs" -> - get_hashtableinfo1(Fd,HashTable#hash_table{objs=val(Fd)}); + get_hashtableinfo1(Fd,HashTable#hash_table{objs=bytes(Fd)}); "depth" -> - get_hashtableinfo1(Fd,HashTable#hash_table{depth=val(Fd)}); + get_hashtableinfo1(Fd,HashTable#hash_table{depth=bytes(Fd)}); "=" ++ _next_tag -> HashTable; Other -> @@ -2353,15 +2414,15 @@ get_indextableinfo(Fd,Name,Start) -> get_indextableinfo1(Fd,IndexTable) -> case line_head(Fd) of "size" -> - get_indextableinfo1(Fd,IndexTable#index_table{size=val(Fd)}); + get_indextableinfo1(Fd,IndexTable#index_table{size=bytes(Fd)}); "used" -> - get_indextableinfo1(Fd,IndexTable#index_table{used=val(Fd)}); + get_indextableinfo1(Fd,IndexTable#index_table{used=bytes(Fd)}); "limit" -> - get_indextableinfo1(Fd,IndexTable#index_table{limit=val(Fd)}); + get_indextableinfo1(Fd,IndexTable#index_table{limit=bytes(Fd)}); "rate" -> - get_indextableinfo1(Fd,IndexTable#index_table{rate=val(Fd)}); + get_indextableinfo1(Fd,IndexTable#index_table{rate=bytes(Fd)}); "entries" -> - get_indextableinfo1(Fd,IndexTable#index_table{entries=val(Fd)}); + get_indextableinfo1(Fd,IndexTable#index_table{entries=bytes(Fd)}); "=" ++ _next_tag -> IndexTable; Other -> @@ -2393,45 +2454,45 @@ get_schedulerinfo(Fd,Name,Start) -> get_schedulerinfo1(Fd,Sched=#sched{details=Ds}) -> case line_head(Fd) of "Current Process" -> - get_schedulerinfo1(Fd,Sched#sched{process=val(Fd, "None")}); + get_schedulerinfo1(Fd,Sched#sched{process=bytes(Fd, "None")}); "Current Port" -> - get_schedulerinfo1(Fd,Sched#sched{port=val(Fd, "None")}); + get_schedulerinfo1(Fd,Sched#sched{port=bytes(Fd, "None")}); "Run Queue Max Length" -> - RQMax = list_to_integer(val(Fd)), + RQMax = list_to_integer(bytes(Fd)), RQ = RQMax + Sched#sched.run_q, get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_max=>RQMax}}); "Run Queue High Length" -> - RQHigh = list_to_integer(val(Fd)), + RQHigh = list_to_integer(bytes(Fd)), RQ = RQHigh + Sched#sched.run_q, get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_high=>RQHigh}}); "Run Queue Normal Length" -> - RQNorm = list_to_integer(val(Fd)), + RQNorm = list_to_integer(bytes(Fd)), RQ = RQNorm + Sched#sched.run_q, get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_norm=>RQNorm}}); "Run Queue Low Length" -> - RQLow = list_to_integer(val(Fd)), + RQLow = list_to_integer(bytes(Fd)), RQ = RQLow + Sched#sched.run_q, get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_low=>RQLow}}); "Run Queue Port Length" -> - RQ = list_to_integer(val(Fd)), + RQ = list_to_integer(bytes(Fd)), get_schedulerinfo1(Fd,Sched#sched{port_q=RQ}); "Scheduler Sleep Info Flags" -> - get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_info=>val(Fd, "None")}}); + get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_info=>bytes(Fd, "None")}}); "Scheduler Sleep Info Aux Work" -> - get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_aux=>val(Fd, "None")}}); + get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_aux=>bytes(Fd, "None")}}); "Run Queue Flags" -> - get_schedulerinfo1(Fd,Sched#sched{details=Ds#{runq_flags=>val(Fd, "None")}}); + get_schedulerinfo1(Fd,Sched#sched{details=Ds#{runq_flags=>bytes(Fd, "None")}}); "Current Process State" -> - get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_state=>val(Fd)}}); + get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_state=>bytes(Fd)}}); "Current Process Internal State" -> - get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_int_state=>val(Fd)}}); + get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_int_state=>bytes(Fd)}}); "Current Process Program counter" -> - get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_prg_cnt=>val(Fd)}}); + get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_prg_cnt=>string(Fd)}}); "Current Process CP" -> - get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_cp=>val(Fd)}}); + get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_cp=>string(Fd)}}); "Current Process Limited Stack Trace" -> %% If there shall be last in scheduler information block Sched#sched{details=get_limited_stack(Fd, 0, Ds)}; @@ -2443,7 +2504,7 @@ get_schedulerinfo1(Fd,Sched=#sched{details=Ds}) -> end. get_limited_stack(Fd, N, Ds) -> - case val(Fd) of + case string(Fd) of Addr = "0x" ++ _ -> get_limited_stack(Fd, N+1, Ds#{{currp_stack, N} => Addr}); "=" ++ _next_tag -> @@ -2509,9 +2570,9 @@ parse_heap_term("Yc"++Line0, Addr, BinAddrAdj, D0) -> %Reference-counted binary. {Offset,":"++Line2} = get_hex(Line1), {Sz,Line} = get_hex(Line2), Binp = Binp0 bor BinAddrAdj, - Term = case gb_trees:lookup(Binp, D0) of - {value,Bin} -> cdvbin(Offset,Sz,Bin); - none -> '#CDVNonexistingBinary' + Term = case lookup_binary_index(Binp) of + [{_,Start}] -> cdvbin(Offset,Sz,{'#CDVBin',Start}); + [] -> '#CDVNonexistingBinary' end, D = gb_trees:insert(Addr, Term, D0), {Term,Line,D}; @@ -2520,15 +2581,14 @@ parse_heap_term("Ys"++Line0, Addr, BinAddrAdj, D0) -> %Sub binary. {Offset,":"++Line2} = get_hex(Line1), {Sz,Line} = get_hex(Line2), Binp = Binp0 bor BinAddrAdj, - Term = case gb_trees:lookup(Binp, D0) of - {value,Bin} -> cdvbin(Offset,Sz,Bin); - none when Binp0=/=Binp -> + Term = case lookup_binary_index(Binp) of + [{_,Start}] -> cdvbin(Offset,Sz,{'#CDVBin',Start}); + [] -> %% Might it be on the heap? - case gb_trees:lookup(Binp0, D0) of + case gb_trees:lookup(Binp, D0) of {value,Bin} -> cdvbin(Offset,Sz,Bin); none -> '#CDVNonexistingBinary' - end; - none -> '#CDVNonexistingBinary' + end end, D = gb_trees:insert(Addr, Term, D0), {Term,Line,D}. @@ -2595,7 +2655,7 @@ skip_dist_ext([C|Cs], KeptCs) -> parse_atom([$A|Line0], D) -> {N,":"++Line1} = get_hex(Line0), {Chars, Line} = get_chars(N, Line1), - {list_to_atom(Chars), Line, D}. + {binary_to_atom(list_to_binary(Chars),utf8), Line, D}. parse_atom_translation_table(0, Line0, As) -> {list_to_tuple(lists:reverse(As)), Line0}; @@ -2614,11 +2674,12 @@ deref_ptr(Ptr, Line, BinAddrAdj, D0) -> end_of_heap -> {['#CDVIncompleteHeap'],Line,D0}; Fd -> - case val(Fd) of + case bytes(Fd) of "="++_ -> 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 @@ -2684,19 +2745,33 @@ get_label([H|T], Acc) -> get_label(T, [H|Acc]). get_binary(Line0) -> - {N,":"++Line} = get_hex(Line0), - do_get_binary(N, Line, []). + case get_hex(Line0) of + {N,":"++Line} -> + do_get_binary(N, Line, [], false); + _ -> + {'#CDVTruncatedBinary',[]} + end. get_binary(Offset,Size,Line0) -> - {_N,":"++Line} = get_hex(Line0), - do_get_binary(Size, lists:sublist(Line,(Offset*2)+1,Size*2), []). - -do_get_binary(0, Line, Acc) -> + case get_hex(Line0) of + {_N,":"++Line} -> + 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, 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}) -> @@ -2704,12 +2779,17 @@ cdvbin(Offset,Size,{'#CDVBin',Pos}) -> cdvbin(Offset,Size,['#CDVBin',_,_,Pos]) -> ['#CDVBin',Offset,Size,Pos]; cdvbin(_,_,'#CDVTruncatedBinary') -> - '#CDVTruncatedBinary'. + '#CDVTruncatedBinary'; +cdvbin(_,_,'#CDVNonexistingBinary') -> + '#CDVNonexistingBinary'. %%----------------------------------------------------------------- -%% Functions for accessing the cdv_dump_index_table -reset_index_table() -> - ets:delete_all_objects(cdv_dump_index_table). +%% Functions for accessing tables +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_heap_file_chars). insert_index(Tag,Id,Pos) -> ets:insert(cdv_dump_index_table,{{Tag,Pos},Id}). @@ -2724,6 +2804,11 @@ lookup_index(Tag,Id) -> count_index(Tag) -> ets:select_count(cdv_dump_index_table,[{{{Tag,'_'},'_'},[],[true]}]). +insert_binary_index(Addr,Pos) -> + ets:insert(cdv_binary_index_table,{Addr,Pos}). + +lookup_binary_index(Addr) -> + ets:lookup(cdv_binary_index_table,Addr). %%----------------------------------------------------------------- %% Convert tags read from crashdump to atoms used as first part of key @@ -2759,7 +2844,7 @@ tag_to_atom("scheduler") -> ?scheduler; tag_to_atom("timer") -> ?timer; tag_to_atom("visible_node") -> ?visible_node; tag_to_atom(UnknownTag) -> - io:format("WARNING: Found unexpected tag:~s~n",[UnknownTag]), + io:format("WARNING: Found unexpected tag:~ts~n",[UnknownTag]), list_to_atom(UnknownTag). %%%----------------------------------------------------------------- @@ -2794,23 +2879,6 @@ to_value_list(Record) -> Values. %%%----------------------------------------------------------------- -%%% Fold over List and report progress in percent. -%%% Report is the text to be presented in the progress dialog. -%%% Acc0 is the initial accumulator and will be passed to Fun as the -%%% second arguement, i.e. Fun = fun(Item,Acc) -> NewAcc end. -progress_foldl(Report,Fun,Acc0,List) -> - init_progress(Report, length(List)), - progress_foldl1(Fun,Acc0,List). - -progress_foldl1(Fun,Acc,[H|T]) -> - update_progress(), - progress_foldl1(Fun,Fun(H,Acc),T); -progress_foldl1(_Fun,Acc,[]) -> - end_progress(), - Acc. - - -%%%----------------------------------------------------------------- %%% Map over List and report progress in percent. %%% Report is the text to be presented in the progress dialog. %%% Distribute the load over a number of processes, and File is opened |