crashdump_viewer.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 2,144 行 · 第 1/5 页
ERL
2,144 行
count_rest_of_line(Fd,Bin,N);count_rest_of_line(Fd,<<_Char:8,Bin/binary>>,N) -> count_rest_of_line(Fd,Bin,N+1);count_rest_of_line(Fd,<<>>,N) -> case get_chunk(Fd) of {ok,Bin} -> count_rest_of_line(Fd,Bin,N); eof -> {eof,N} end.get_n_lines_of_tag(Fd,N) -> case get_chunk(Fd) of {ok,Bin} -> {AllOrPart,Rest,Lines} = get_n_lines_of_tag(Fd,N,Bin,[]), {AllOrPart,N-Rest,Lines}; eof -> empty end.get_n_lines_of_tag(Fd,N,<<"\n=",_/binary>>=Bin,Acc) -> put_chunk(Fd,Bin), {all,N-1,lists:reverse(Acc)};get_n_lines_of_tag(Fd,0,Bin,Acc) -> put_chunk(Fd,Bin), {part,0,lists:reverse(Acc)};get_n_lines_of_tag(Fd,N,<<$\n:8,Bin/binary>>,Acc) -> get_n_lines_of_tag(Fd,N-1,Bin,[$\n|Acc]);get_n_lines_of_tag(Fd,N,<<$\r:8,Bin/binary>>,Acc) -> get_n_lines_of_tag(Fd,N,Bin,Acc);get_n_lines_of_tag(Fd,N,<<Char:8,Bin/binary>>,Acc) -> get_n_lines_of_tag(Fd,N,Bin,[Char|Acc]);get_n_lines_of_tag(Fd,N,<<>>,Acc) -> case get_chunk(Fd) of {ok,Bin} -> get_n_lines_of_tag(Fd,N,Bin,Acc); eof -> case Acc of [$\n|_] -> {all,N,lists:reverse(Acc)}; _ -> {all,N-1,lists:reverse(Acc)} end end.count_rest_of_tag(Fd) -> case get_chunk(Fd) of {ok,Bin} -> count_rest_of_tag(Fd,Bin,0); eof -> 0 end.count_rest_of_tag(Fd,<<"\n=",Bin/binary>>,N) -> put_chunk(Fd,Bin), N;count_rest_of_tag(Fd,<<$\r:8,Bin/binary>>,N) -> count_rest_of_tag(Fd,Bin,N);count_rest_of_tag(Fd,<<_Char:8,Bin/binary>>,N) -> count_rest_of_tag(Fd,Bin,N+1);count_rest_of_tag(Fd,<<>>,N) -> case get_chunk(Fd) of {ok,Bin} -> count_rest_of_tag(Fd,Bin,N); eof -> N end.split(Str) -> split($ ,Str,[]). split(Char,Str) -> split(Char,Str,[]).split(Char,[Char|Str],Acc) -> % match Char {lists:reverse(Acc),Str};split(_Char,[$\r,$\n|Str],Acc) -> % new line {lists:reverse(Acc),Str};split(_Char,[$\n|Str],Acc) -> % new line {lists:reverse(Acc),Str};split(Char,[H|T],Acc) -> split(Char,T,[H|Acc]);split(_Char,[],Acc) -> {lists:reverse(Acc),[]}.size_or_term(Fd) -> size_or_term(Fd,get(pos)).size_or_term(Fd,Pos) -> case count_rest_of_line(Fd) of {eof,Size} -> {size,true,Size,Pos}; Size when Size > ?max_display_size -> {size,false,Size,Pos}; _Size -> {ok,Pos} = pos_bof(Fd,Pos), val(Fd) end.%%%-----------------------------------------------------------------%%% get_value(Key,List) -> case lists:keysearch(Key,1,List) of {value,{Key,Value}} -> {ok,Value}; false -> error end.parse_vsn_str([],WS,false) -> %% If the log is translated, crashdump_translate might have written %% shared_heap=true in dictionary. case erase(shared_heap) of true -> {WS,true}; _ -> {WS,false} end;parse_vsn_str([],WS,SH) -> {WS,SH};parse_vsn_str(Str,WS,SH) -> case Str of "[64-bit]" ++ Rest -> case SH of false -> parse_vsn_str(Rest,8,false); _ -> {8,SH} end; "[shared heap]" ++ Rest -> case WS of 4 -> parse_vsn_str(Rest,WS,true); _ -> {WS,true} end; [_Char|Rest] -> parse_vsn_str(Rest,WS,SH) end.%%%-----------------------------------------------------------------%%% initial_menu() -> insert_items( [menu_item(0, {"./general_info","General information"},0), menu_item(0, {"./processes","Processes"}, 0), menu_item(0, {"./ports","Ports"}, 0), menu_item(0, {"./ets_tables","ETS tables"}, 0), menu_item(0, {"./timers","Timers"}, 0), menu_item(0, {"./fun_table","Fun table"}, 0), menu_item(0, {"./atoms","Atoms"}, 0), menu_item(0, {"./dist_info","Distribution information"}, 0), menu_item(0, {"./loaded_modules","Loaded modules"}, 0), menu_item(2, "Internal Tables", 0), menu_item(0, {"./hash_tables","Hash tables"}, 1), menu_item(0, {"./index_tables","Index tables"}, 1), menu_item(3, "Memory information", 0), menu_item(0, {"./memory","Memory"}, 1), menu_item(0, {"./allocated_areas","Allocated areas"}, 1), menu_item(0, {"./allocator_info","Allocator information"}, 1), menu_item(2, "Documentation", 0), menu_item(0, {"/crashdump_doc/crashdump_help.html", "Crashdump Viewer help"}, 1,"doc"), menu_item(0, {"/crashdump_erts_doc/crash_dump.html", "How to interpret Erlang crashdumps"}, 1,"doc")]). menu_item(Children,Text,Depth) -> menu_item(Children,Text,Depth,"main").menu_item(Children,Text,Depth,Target) -> #menu_item{picture=get_pic(Children), text=Text, depth=Depth, children=Children, state=if Depth==0 -> true; true -> false end, target=Target}.insert_items(Items) -> insert_items(Items,1).insert_items([Item|Items],Index) -> ets:insert(cdv_menu_table,Item#menu_item{index=Index}), insert_items(Items,Index+1);insert_items([],_) -> ok.get_pic(0) -> "";get_pic(_) -> "/crashdump_viewer/collapsd.gif".do_toggle(Index) -> [Item]= ets:lookup(cdv_menu_table,Index), case toggle_children(Index,Index+Item#menu_item.children, Item#menu_item.depth+1,undefined) of true -> ets:insert(cdv_menu_table, Item#menu_item{picture= "/crashdump_viewer/exploded.gif"}); false -> ets:insert(cdv_menu_table, Item#menu_item{picture= "/crashdump_viewer/collapsd.gif"}) end.toggle_children(Index,Max,_Depth,ToggleState) when Index>Max-> ToggleState;toggle_children(Index,Max,Depth,ToggleState) -> case ets:lookup(cdv_menu_table,Index+1) of [#menu_item{depth=Depth}=Child] -> NewState = not Child#menu_item.state, ets:insert(cdv_menu_table,Child#menu_item{state=NewState}), toggle_children(Index+1,Max,Depth,NewState); _ -> toggle_children(Index+1,Max,Depth,ToggleState) end.%%%-----------------------------------------------------------------%%% Traverse crash dump and insert index in table for each heading%%% %%% This function is executed in a background process in order to%%% avoid a timeout in the web browser. The browser displays "Please%%% wait..." while this is going on.%%%%%% Variable written to process dictionary in this function are copied%%% to the crashdump_viewer_server when the function is completed (see%%% background_done/1).read_file(File) -> case file:read_file_info(File) of {ok,#file_info{type=regular,access=FileA}} when FileA=:=read; FileA=:=read_write -> Fd = open(File), case read(Fd) of {ok,<<$=:8,TagAndRest/binary>>} -> {Tag,Id,Rest,N1} = tag(Fd,TagAndRest,1), case Tag of "=erl_crash_dump" -> ets:delete_all_objects(cdv_dump_index_table), ets:insert(cdv_dump_index_table,{Tag,Id,N1+1}), put(last_tag,{Tag,""}), Status = background_status(processing,File), background_status(Status), indexify(Fd,Rest,N1), check_if_truncated(), initial_menu(), Binaries = read_binaries(Fd), R = crashdump_viewer_html:start_page(), close(Fd), background_done({R,File,Binaries}); _Other -> R = crashdump_viewer_html:error( "~s is not an Erlang crash dump~n", [File]), close(Fd), background_done({R,undefined,undefined}) end; {ok,<<"<Erlang crash dump>",_Rest/binary>>} -> %% old version - translate close(Fd), case file:read_file_info(filename:dirname(File)) of {ok,#file_info{access=DirA}} when DirA=:=write; DirA=:=read_write -> TranslatedFile = File++".translated", case file:read_file_info(TranslatedFile) of {error,enoent} -> do_translate(File,TranslatedFile); {ok,#file_info{access=DirA}} when DirA=:=write; DirA=:=read_write -> do_translate(File,TranslatedFile); _ -> R = get_translated_filename(TranslatedFile), background_done({R,File,undefined}) end; {ok,_FileInfo} -> R = get_translated_filename(File), background_done({R,File,undefined}) end; _Other -> R = crashdump_viewer_html:error( "~s is not an Erlang crash dump~n", [File]), close(Fd), background_done({R,undefined,undefined}) end; _other -> R = crashdump_viewer_html:error("~s is not an Erlang crash dump~n", [File]), background_done({R,undefined,undefined}) end.%%% Translate an old type crashdump.%%% This function is run in a background process - see more info in%%% comments for read_file/1 above.do_translate(File,TranslatedFile) -> Status = background_status(translating,File), background_status(Status), case crashdump_translate:old2new(File,TranslatedFile) of ok -> read_file(TranslatedFile); Error -> R=crashdump_viewer_html:error("Can not translate ~s:~n~p~n", [File,Error]), background_done({R,undefined,undefined}) end.get_translated_filename(File) -> crashdump_viewer_html:get_translated_filename_frame(File). indexify(Fd,<<"\n=",TagAndRest/binary>>,N) -> {Tag,Id,Rest,N1} = tag(Fd,TagAndRest,N+2), ets:insert(cdv_dump_index_table,{Tag,Id,N1+1}), % +1 to get past newline put(last_tag,{Tag,Id}), indexify(Fd,Rest,N1);indexify(Fd,<<>>,N) -> case read(Fd) of {ok,Chunk} when is_binary(Chunk) -> indexify(Fd,Chunk,N); eof -> eof end;indexify(Fd,<<$\n>>,N) -> %% This clause is needed in case the chunk ends with a newline and %% the next chunk starts with a tag (i.e. "\n=....") case read(Fd) of {ok,Chunk} when is_binary(Chunk) -> indexify(Fd,<<$\n,Chunk/binary>>,N); eof -> eof end;indexify(Fd,<<_Char:8,Rest/binary>>,N) -> indexify(Fd,Rest,N+1).tag(Fd,Bin,N) -> tag(Fd,Bin,N,[],[],tag).tag(_Fd,<<$\n:8,_/binary>>=Rest,N,Gat,Di,_Now) -> {[$=|lists:reverse(Gat)],lists:reverse(Di),Rest,N};tag(Fd,<<$\r:8,Rest/binary>>,N,Gat,Di,Now) -> tag(Fd,Rest,N+1,Gat,Di,Now);tag(Fd,<<$::8,IdAndRest/binary>>,N,Gat,Di,tag) -> tag(Fd,IdAndRest,N+1,Gat,Di,id);tag(Fd,<<Char:8,Rest/binary>>,N,Gat,Di,tag) -> tag(Fd,Rest,N+1,[Char|Gat],Di,tag);tag(Fd,<<Char:8,Rest/binary>>,N,Gat,Di,id) -> tag(Fd,Rest,N+1,Gat,[Char|Di],id);tag(Fd,<<>>,N,Gat,Di,Now) -> case read(Fd) of {ok,Chunk} when is_binary(Chunk) -> tag(Fd,Chunk,N,Gat,Di,Now); eof -> {[$=|lists:reverse(Gat)],lists:reverse(Di),<<>>,N} end.check_if_truncated() -> case get(last_tag) of {"=end",_} -> put(truncated,false), put(truncated_proc,false); TruncatedTag -> put(truncated,true), find_truncated_proc(TruncatedTag) end. find_truncated_proc({"=atom",_Id}) -> put(truncated_proc,false);find_truncated_proc({Tag,Pid}) -> case is_proc_tag(Tag) of true -> put(truncated_proc,Pid); false -> %% This means that the dump is truncated between "=proc" and %% "=proc_heap" => memory info is missing for all procs. put(truncated_proc,"<0.0.0>") end.is_proc_tag(Tag) when Tag=="=proc"; Tag=="=proc_dictionary"; Tag=="=proc_messages"; Tag=="=proc_dictionary"; Tag=="=debug_proc_dictionary"; Tag=="=proc_stack"; Tag=="=proc_heap" -> true;is_proc_tag(_) -> false.related_tags("Atoms") -> ["=atoms","=num_atoms"].%%% Inform the crashdump_viewer_server that a background job is completed.background_done(Result) -> Dict = get(), cast({background_done,Result,Dict}). background_status(Status) -> cast({background_status,Status}).%%%-----------------------------------------------------------------%%% Functions for reading information from the dumpgeneral_info(File) -> [{"=erl_crash_dump",_Id,Start}] = ets:lookup(cdv_dump_index_table,"=erl_crash_dump"), Fd = open(File), pos_bof(Fd,Start), Created = case get_rest_of_line(Fd) of {eof,SomeOfLine} -> SomeOfLine; WholeLine -> WholeLine end, GI0 = get_general_info(Fd,#general_info{created=Created,_=?space}), GI = case GI0#general_info.num_atoms of ?space -> GI0#general_info{num_atoms=get_num_atoms(Fd)}; _ -> GI0 end, {MemTot,MemMax} = case ets:lookup(cdv_dump_index_table,"=memory") of [{"=memory",_,MemStart}] -> pos_bof(Fd,MemStart), Memory = get_meminfo(Fd,[]), Tot = case lists:keysearch("total",1,Memory) of {value,{_,T}} -> T; false -> "" end, Max = case lists:keysearch("maximum",1,Memory) of {value,{_,M}} -> M; false -> "" end, {Tot,Max}; _ -> {"",""} end, close(Fd), {NumProcs,NumEts,NumFuns} = count(), NodeName = case ets:lookup(cdv_dump_index_table,"=node") of [{"=node",N,_Start}] -> N; [] -> case ets:lookup(cdv_dump_index_table,"=no_distribution") of [_] -> "nonode@nohost"; [] -> "unknown" end end,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?