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 + -
显示快捷键?