crashdump_viewer.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 2,144 行 · 第 1/5 页
ERL
2,144 行
InstrInfo = case ets:member(cdv_dump_index_table,"=old_instr_data") of true -> old_instr_data; false -> case ets:member(cdv_dump_index_table,"=instr_data") of true -> instr_data; false -> false end end, GI#general_info{node_name=NodeName, num_procs=integer_to_list(NumProcs), num_ets=integer_to_list(NumEts), num_fun=integer_to_list(NumFuns), mem_tot=MemTot, mem_max=MemMax, instr_info=InstrInfo}.get_general_info(Fd,GenInfo) -> case line_head(Fd) of "Slogan" -> get_general_info(Fd,GenInfo#general_info{slogan=val(Fd)}); "System version" -> get_general_info(Fd,GenInfo#general_info{system_vsn=val(Fd)}); "Compiled" -> get_general_info(Fd,GenInfo#general_info{compile_time=val(Fd)}); "Atoms" -> get_general_info(Fd,GenInfo#general_info{num_atoms=val(Fd)}); "=" ++ _next_tag -> GenInfo; Other -> unexpected(Fd,Other,"general information"), GenInfo end.get_num_atoms(Fd) -> case ets:match(cdv_dump_index_table,{"=hash_table","atom_tab",'$1'}) of [[Pos]] -> pos_bof(Fd,Pos), skip_rest_of_line(Fd), % size skip_rest_of_line(Fd), % used case line_head(Fd) of "objs" -> val(Fd); _1 -> get_num_atoms2() end; [] -> get_num_atoms2() end.get_num_atoms2() -> case ets:lookup(cdv_dump_index_table,"=num_atoms") of [] -> ?space; [{"=num_atoms",NA,_Pos}] -> %% If dump is translated this will exist case get(truncated) of true -> [NA," (visible in dump)"]; % might be more false -> NA end end.count() -> {ets:select_count(cdv_dump_index_table,count_ms("=proc")), ets:select_count(cdv_dump_index_table,count_ms("=ets")), ets:select_count(cdv_dump_index_table,count_ms("=fun"))}.count_ms(Tag) -> [{{Tag,'_','_'},[],[true]}].procs_summary(File) -> AllProcs = ets:lookup(cdv_dump_index_table,"=proc"), Fd = open(File), R = lists:map(fun({"=proc",Pid,Start}) -> pos_bof(Fd,Start), get_procinfo(Fd,fun main_procinfo/4, ?initial_proc_record(Pid)) end, AllProcs), close(Fd), R.get_proc_details(File,Pid) -> DumpVsn = ets:lookup_element(cdv_dump_index_table,"=erl_crash_dump",2), case ets:match(cdv_dump_index_table,{"=proc",Pid,'$1'}) of [[Start]] -> Fd = open(File), pos_bof(Fd,Start), Proc0 = case DumpVsn of "0.0" -> %% Old version (translated) ?initial_proc_record(Pid); _ -> (?initial_proc_record(Pid))#proc{ stack_dump=if_exist("=proc_stack",Pid), msg_q=if_exist("=proc_messages",Pid), dict=if_exist("=proc_dictionary",Pid), debug_dict=if_exist("=debug_proc_dictionary",Pid)} end, Proc = get_procinfo(Fd,fun all_procinfo/4,Proc0), close(Fd), {ok,Proc}; _ -> case maybe_other_node(File,Pid) of {other_node,Type,Node} -> Info = "The process you are searching for was residing on " "a remote node. No process information is available. " "Information about the remote node is show below.", {other_node,{Type,Info,Node}}; not_found -> not_found end end.if_exist(Tag,Key) -> case ets:select_count(cdv_dump_index_table,[{{Tag,Key,'_'},[],[true]}]) of 0 -> Tag1 = case is_proc_tag(Tag) of true -> "=proc"; false -> Tag end, case truncated_here({Tag1,Key}) of true -> truncated; false -> ?space end; _ -> expand end.get_procinfo(Fd,Fun,Proc) -> case line_head(Fd) of "State" -> State = case val(Fd) of "Garbing" -> "Garbing\n(limited info)"; State0 -> State0 end, get_procinfo(Fd,Fun,Proc#proc{state=State}); "Name" -> get_procinfo(Fd,Fun,Proc#proc{name=val(Fd)}); "Spawned as" -> IF = val(Fd), case Proc#proc.name of ?space -> get_procinfo(Fd,Fun,Proc#proc{name=IF,init_func=IF}); _ -> get_procinfo(Fd,Fun,Proc#proc{init_func=IF}) end; "Spawned by" -> case val(Fd) of "[]" -> get_procinfo(Fd,Fun,Proc); Parent -> get_procinfo(Fd,Fun,Proc#proc{parent=Parent}) end; "Started" -> get_procinfo(Fd,Fun,Proc#proc{start_time=val(Fd)}); "Last scheduled in for" -> get_procinfo(Fd,Fun,Proc#proc{current_func= {"Last scheduled in for", val(Fd)}}); "Current call" -> get_procinfo(Fd,Fun,Proc#proc{current_func={"Current call", val(Fd)}}); "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))}); "Reductions" -> %% stored as integer so we can sort on it get_procinfo(Fd,Fun,Proc#proc{reds=list_to_integer(val(Fd))}); "Number of heap fragments" -> get_procinfo(Fd,Fun,Proc#proc{num_heap_frag=val(Fd)}); "Heap fragment data" -> get_procinfo(Fd,Fun,Proc#proc{heap_frag_data=val(Fd)}); Stack when Stack=:="Stack+heap"; Stack=:="Stack" -> %% stored as integer so we can sort on it get_procinfo(Fd,Fun,Proc#proc{stack_heap= list_to_integer(val(Fd))}); "OldHeap" -> get_procinfo(Fd,Fun,Proc#proc{old_heap=val(Fd)}); "Heap unused" -> get_procinfo(Fd,Fun,Proc#proc{heap_unused=val(Fd)}); "OldHeap unused" -> get_procinfo(Fd,Fun,Proc#proc{old_heap_unused=val(Fd)}); "New heap start" -> get_procinfo(Fd,Fun,Proc#proc{new_heap_start=val(Fd)}); "New heap top" -> get_procinfo(Fd,Fun,Proc#proc{new_heap_top=val(Fd)}); "Stack top" -> get_procinfo(Fd,Fun,Proc#proc{stack_top=val(Fd)}); "Stack end" -> get_procinfo(Fd,Fun,Proc#proc{stack_end=val(Fd)}); "Old heap start" -> get_procinfo(Fd,Fun,Proc#proc{old_heap_start=val(Fd)}); "Old heap top" -> get_procinfo(Fd,Fun,Proc#proc{old_heap_top=val(Fd)}); "Old heap end" -> get_procinfo(Fd,Fun,Proc#proc{old_heap_end=val(Fd)}); {eof,_} -> Proc; % truncated file Other -> Fun(Fd,Fun,Proc,Other) end.main_procinfo(Fd,Fun,Proc,LineHead) -> case LineHead of "Stack dump" -> %% This is the last element in older dumps (DumpVsn=0.0) Proc; "=" ++ _next_tag -> %% DumpVsn=0.1 or newer: No stack dump here Proc; "arity = " ++ _ -> %%! Temporary workaround get_procinfo(Fd,Fun,Proc); _Other -> skip_rest_of_line(Fd), get_procinfo(Fd,Fun,Proc) end.all_procinfo(Fd,Fun,Proc,LineHead) -> case LineHead of "Message queue" -> get_procinfo(Fd,Fun,Proc#proc{msg_q=size_or_term(Fd)}); "Last calls" -> R = case size_or_term(Fd) of SizeThing when is_tuple(SizeThing) -> Proc#proc{last_calls=SizeThing}; Term -> Proc#proc{last_calls=replace_all($ ,$\n,Term,[])} end, get_procinfo(Fd,Fun,R); "Link list" -> get_procinfo(Fd,Fun,Proc#proc{links=val(Fd)}); "Program counter" -> get_procinfo(Fd,Fun,Proc#proc{prog_count=val(Fd)}); "CP" -> get_procinfo(Fd,Fun,Proc#proc{cp=val(Fd)}); "arity = " ++ Arity -> %%! Temporary workaround get_procinfo(Fd,Fun,Proc#proc{arity=Arity--"\r\n"}); "Dictionary" -> get_procinfo(Fd,Fun,Proc#proc{dict=size_or_term(Fd)}); "$Dictionary" -> get_procinfo(Fd,Fun,Proc#proc{debug_dict=size_or_term(Fd)}); "Stack dump" -> %% This is the last element in older dumps (DumpVsn=0.0) get_stack_dump(Fd,Proc); "=" ++ _next_tag -> %% DumpVsn=0.1 or newer: No stack dump here Proc; Other -> unexpected(Fd,Other,"process info"), get_procinfo(Fd,Fun,Proc) end.get_stack_dump(Fd,Proc) -> %% Always show stackdump as "Expand" link Pos = get(pos), Size = count_rest_of_tag(Fd), Proc#proc{stack_dump={size,true,Size,Pos}}.maybe_other_node(File,Id) -> Channel = case split($.,Id) of {"<" ++ N, _Rest} -> N; {"#Port<" ++ N, _Rest} -> N end, Ms = ets:fun2ms( fun({Tag,Id,Start}) when Tag=:="=visible_node", Id=:=Channel -> {"Visible Node",Start}; ({Tag,Id,Start}) when Tag=:="=hidden_node", Id=:=Channel -> {"Hidden Node",Start}; ({Tag,Id,Start}) when Tag=:="=not_connected", Id=:=Channel -> {"Not Connected Node",Start} end), case ets:select(cdv_dump_index_table,Ms) of [] -> not_found; [{Type,Pos}] -> Fd = open(File), NodeInfo = get_nodeinfo(Fd,Channel,Pos), close(Fd), {other_node,Type,NodeInfo} end.expand_memory(File,What,Pid,Binaries) -> Fd = open(File), put(fd,Fd), Dict = read_heap(Fd,Pid,Binaries), Expanded = case What of "StackDump" -> read_stack_dump(Fd,Pid,Dict); "MsgQueue" -> read_messages(Fd,Pid,Dict); "Dictionary" -> read_dictionary(Fd,"=proc_dictionary",Pid,Dict); "DebugDictionary" -> read_dictionary(Fd,"=debug_proc_dictionary",Pid,Dict) end, erase(fd), close(Fd), Expanded. %%%%%% Read binaries.%%%read_binaries(Fd) -> AllBinaries = ets:match(cdv_dump_index_table,{"=binary",'$1','$2'}), read_binaries(Fd,AllBinaries, gb_trees:empty()).read_binaries(Fd,[[Addr0,Pos]|Bins],Dict0) -> pos_bof(Fd,Pos), {Addr,_} = get_hex(Addr0), Dict = case line_head(Fd) of {eof,_} -> gb_trees:enter(Addr,'#CDVTruncatedBinary',Dict0); Size0 -> {Size,_} = get_hex(Size0), if Size > ?max_display_binary_size -> gb_trees:enter(Addr,{'#CDVTooBig',binary,Pos},Dict0); true -> pos_bof(Fd,Pos), Line = val(Fd), parse_binary(Addr,Line,Dict0) end end, read_binaries(Fd,Bins,Dict);read_binaries(_Fd,[],Dict) -> Dict.parse_binary(Addr, Line0, Dict) -> case get_hex(Line0) of {N,":"++Line1} -> {Bin,Line} = get_binary(N, Line1, []), [] = skip_blanks(Line), gb_trees:enter(Addr, Bin, Dict); {_N,[]} -> %% If the dump is truncated before the ':' in this line, then %% line_head/1 might not discover it (if a \n has been inserted %% somehow???) gb_trees:enter(Addr,'#CDVTruncatedBinary',Dict) end.%%%%%% Read top level section.%%%read_stack_dump(Fd,Pid,Dict) -> case ets:match(cdv_dump_index_table,{"=proc_stack",Pid,'$1'}) of [[Start]] -> pos_bof(Fd,Start), read_stack_dump1(Fd,Dict,[]); [] -> [] end.read_stack_dump1(Fd,Dict,Acc) -> %% This function is never called if the dump is truncated in "=proc_heap:Pid" case val(Fd) of "=" ++ _next_tag -> lists:reverse(Acc); Line -> Stack = parse_top(Line,Dict), read_stack_dump1(Fd,Dict,[Stack|Acc]) end.parse_top(Line0, D) -> {Label,Line1} = get_label(Line0), {Term,Line,D} = parse_term(Line1, D), [] = skip_blanks(Line), {Label,Term}.%%%%%% Read message queue.%%%read_messages(Fd,Pid,Dict) -> case ets:match(cdv_dump_index_table,{"=proc_messages",Pid,'$1'}) of [[Start]] -> pos_bof(Fd,Start), read_messages1(Fd,Dict,[]); [] -> [] end.read_messages1(Fd,Dict,Acc) -> %% This function is never called if the dump is truncated in "=proc_heap:Pid" case val(Fd) of "=" ++ _next_tag -> lists:reverse(Acc); Line -> Msg = parse_message(Line,Dict), read_messages1(Fd,Dict,[Msg|Acc]) end. parse_message(Line0, D) -> {Msg,":"++Line1,_} = parse_term(Line0, D), {Token,Line,_} = parse_term(Line1, D), [] = skip_blanks(Line), {Msg,Token}. %%%%%% Read process dictionary%%%read_dictionary(Fd,Tag,Pid,Dict) -> case ets:match(cdv_dump_index_table,{Tag,Pid,'$1'}) of [[Start]] -> pos_bof(Fd,Start), read_dictionary1(Fd,Dict,[]); [] -> [] end.read_dictionary1(Fd,Dict,Acc) -> %% This function is never called if the dump is truncated in "=proc_heap:Pid" case val(Fd) of "=" ++ _next_tag -> lists:reverse(Acc); Line -> Msg = parse_dictionary(Line,Dict), read_dictionary1(Fd,Dict,[Msg|Acc]) end.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?