crashdump_viewer.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 2,144 行 · 第 1/5 页

ERL
2,144
字号
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %%     $Id$%%-module(crashdump_viewer).%% %% This module is the main module in the crashdump viewer. It implements%% the server started by webtool and the API for the crashdump viewer tool.%% %% All functions in the API except configData/0 and start_link/0 are %% called from HTML pages via erl_scheme.%% %% Tables%% ------%% cdv_menu_table: This table holds the menu which is presented in the left%% frame of the crashdump viewer page. Each element in the table represents%% one meny item, and the state of the item indicates if it is presently %% visible or not.%% %% cdv_dump_index_table: This table holds all tags read from the crashdump.%% Each tag indicates where the information about a specific item starts.%% The table entry for a tag includes the start and end positions for%% this item-information. All tags start with a "=" at the beginning of%% a line.%%%% Process state%% -------------%% file: The name of the crashdump currently viewed.%% procs_summary: Process summary represented by a list of %% #proc records. This is used for efficiency reasons when sorting%% the process summary table instead of reading all processes from%% the dump again.%% sorted: atom(), indicated what item was last sorted in process summary.%% This is needed so reverse sorting can be done.%% shared_heap: 'true' if crashdump comes from a system running shared heap,%% else 'false'.%% 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-export([start/0,stop/0]).%% Webtool API-export([configData/0,	 start_link/0]).-export([start_page/2,	 read_file_frame/2,	 read_file/2,	 translate/2,	 redirect/2,	 filename_frame/2,	 menu_frame/2,	 initial_info_frame/2,	 toggle/2,	 general_info/2,	 processes/2,	 proc_details/2,	 ports/2,	 ets_tables/2,	 timers/2,	 fun_table/2,	 atoms/2,	 dist_info/2,	 loaded_modules/2,	 loaded_mod_details/2,	 memory/2,	 allocated_areas/2,	 allocator_info/2,	 hash_tables/2,	 index_tables/2,	 sort_procs/2,	 expand/2,	 expand_binary/2,	 expand_memory/2,	 next/2]).%% gen_server callbacks-export([init/1, handle_call/3, handle_cast/2, handle_info/2, 	 terminate/2, code_change/3]).%% Debug support-export([debug/1,stop_debug/0]).-include("crashdump_viewer.hrl").-include_lib("kernel/include/file.hrl").-include_lib("stdlib/include/ms_transform.hrl").-define(START_PAGE,"/cdv_erl/crashdump_viewer/start_page").-define(READ_FILE_PAGE,"/cdv_erl/crashdump_viewer/read_file?path=").-define(SERVER, crashdump_viewer_server).-define(call_timeout,3600000).-define(chunk_size,1000). % number of bytes read from crashdump at a time-define(max_line_size,100). % max number of bytes (i.e. characters) the			    % line_head/1 function can return-define(max_display_size,500). % max number of bytes that will be directly				% displayed. If e.g. msg_q is longer than				% this, it must be explicitly expanded.-define(max_display_binary_size,50). % max size of a binary that will be				      % directly displayed.-define(initial_proc_record(Pid),	#proc{pid=Pid,	      %% msg_q_len, reds and stack_heap are integers because it must 	      %% be possible to sort on them. All other fields are strings	      msg_q_len=0,reds=0,stack_heap=0,	      %% for old dumps start_time, parent and number of heap frament	      %% does not exist	      start_time="unknown",	      parent="unknown",	      num_heap_frag="unknown", 	      %% current_func can be both "current function" and	      %% "last scheduled in for"	      current_func={"Current Function",?space},	      %% stack_dump, message queue and dictionaries should only be 	      %% displayed as a link to "Expand" (if dump is from OTP R9B 	      %% or newer)	      _=?space}).-record(state,{file,procs_summary,sorted,shared_heap=false,	       wordsize=4,num_atoms="unknown",binaries,bg_status}).%%%-----------------------------------------------------------------%%% Debugging%% Start tracing with%% debug(Functions).%% Functions = local | global | FunctionList%% FunctionList = [Function]%% Function = {FunctionName,Arity} | FunctionNamedebug(F) ->     ttb:tracer(all,[{file,"cdv"}]), % tracing all nodes    ttb:p(all,[call,timestamp]),    MS = [{'_',[],[{return_trace},{message,{caller}}]}],    tp(F,MS),    ttb:ctp(?MODULE,stop_debug), % don't want tracing of the stop_debug func    ok.tp([{M,F,A}|T],MS) -> % mod:func/arity    ttb:tpl(M,F,A,MS),    tp(T,MS);tp([{M,F}|T],MS) -> % mod:func    ttb:tpl(M,F,MS),    tp(T,MS);tp([M|T],MS) -> % mod    ttb:tp(M,MS), % only exported    tp(T,MS);tp([],_MS) ->    ok.stop_debug() ->    ttb:stop([format]).%%%-----------------------------------------------------------------%%% User APIstart() ->    webtool:start(),    webtool:start_tools([],"app=crashdump_viewer"),    ok.stop() ->    webtool:stop_tools([],"app=crashdump_viewer"),    webtool:stop().%%%-----------------------------------------------------------------%%% Return config data used by webtoolconfigData() ->    Dir = filename:join(code:priv_dir(observer),"crashdump_viewer"),    {crashdump_viewer,     [{web_data,{"CrashDumpViewer",?START_PAGE}},      {alias,{"/crashdump_viewer",Dir}},      {alias,{"/crashdump_erts_doc",erts_docdir()}},      {alias,{"/crashdump_doc",cdv_docdir()}},      {alias,{erl_alias,"/cdv_erl",[?MODULE]}},      {start,{child,{{local,?SERVER},		     {?MODULE,start_link,[]},		     permanent,100,worker,[?MODULE]}}}	      ]}.erts_docdir() ->    ErtsVsn = erlang:system_info(version),    RootDir = code:root_dir(),    VsnErtsDir = filename:join(RootDir,"erts-"++ErtsVsn),    DocDir = filename:join(["doc","html"]),    case filelib:is_dir(VsnErtsDir) of	true ->	    filename:join(VsnErtsDir,DocDir);	false ->	    %% So this can be run in clearcase	    filename:join([RootDir,"erts",DocDir])    end.cdv_docdir() ->    ObserverDir = code:lib_dir(observer),    filename:join([ObserverDir,"doc","html"]).%%====================================================================%% External functions%%====================================================================%%%--------------------------------------------------------------------%%% Start the serverstart_link() ->    case whereis(?SERVER) of	undefined ->	    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []);	Pid ->	    {ok,Pid}    end.%%%-----------------------------------------------------------------%%% If crashdump_viewer is just started, show welcome frame. Else%%% show menu and general_infostart_page(_Env,_Input) ->    call(start_page).%%%-----------------------------------------------------------------%%% Display the form for entering the file name for the crashdump%%% to view.read_file_frame(_Env,_Input) ->    crashdump_viewer_html:read_file_frame().%%%-----------------------------------------------------------------%%% Called when the 'ok' button is clicked after entering the dump%%% file name.read_file(_Env,Input) ->    call({read_file,Input}).%%%-----------------------------------------------------------------%%% Called when the 'ok' button is clicked after entering a new%%% name for the translated file (R7/R8/R9B)translate(_Env,Input) ->    call({translate,Input}).%%%-----------------------------------------------------------------%%% The topmost frame of the main page. Called when a crashdump is%%% loaded.filename_frame(_Env,_Input) ->    call(filename_frame).%%%-----------------------------------------------------------------%%% The initial information frame. Called when a crashdump is loaded.initial_info_frame(_Env,_Input) ->    call(initial_info_frame).%%%-----------------------------------------------------------------%%% The left frame of the main page. Called when a crashdump is%%% loaded.menu_frame(_Env,_Input) ->    crashdump_viewer_html:menu_frame().%%%-----------------------------------------------------------------%%% Called when the collapsed or exploded picture in the menu is %%% clicked.toggle(_Env,Input) ->    call({toggle,Input}).%%%-----------------------------------------------------------------%%% The following functions are called when menu items are clicked.general_info(_Env,_Input) ->    call(general_info).processes(_Env,_Input) ->    call(procs_summary).ports(_Env,Input) -> % this is also called when a link to a port is clicked    call({ports,Input}).ets_tables(_Env,Input) ->    call({ets_tables,Input}).timers(_Env,Input) ->    call({timers,Input}).fun_table(_Env,_Input) ->    call(funs).atoms(_Env,_Input) ->    call(atoms).dist_info(_Env,_Input) ->    call(dist_info).loaded_modules(_Env,_Input) ->    call(loaded_mods).loaded_mod_details(_Env,Input) ->    call({loaded_mod_details,Input}).memory(_Env,_Input) ->    call(memory).allocated_areas(_Env,_Input) ->    call(allocated_areas).allocator_info(_Env,_Input) ->    call(allocator_info).hash_tables(_Env,_Input) ->    call(hash_tables).index_tables(_Env,_Input) ->    call(index_tables).%%%-----------------------------------------------------------------%%% Called when a link to a process (Pid) is clicked.proc_details(_Env,Input) ->    call({proc_details,Input}).%%%-----------------------------------------------------------------%%% Called when one of the headings in the process summary table are%%% clicked. It sorts the processes by the clicked heading.sort_procs(_Env,Input) ->    call({sort_procs,Input}).%%%-----------------------------------------------------------------%%% Called when the "Expand" link in a call stack (Last Calls) is%%% clicked.expand(_Env,Input) ->    call({expand,Input}).%%%-----------------------------------------------------------------%%% Called when the "Expand" link in a stack dump, message queue or %%% dictionary is clicked.expand_memory(_Env,Input) ->    call({expand_memory,Input}).%%%-----------------------------------------------------------------%%% Called when "<< xxx bytes>>" link in a stack dump, message queue or %%% dictionary is clicked.expand_binary(_Env,Input) ->    call({expand_binary,Input}).%%%-----------------------------------------------------------------%%% Called when the "Next" link under atoms is clicked.next(_Env,Input) ->    call({next,Input}).%%%-----------------------------------------------------------------%%% Called on regular intervals while waiting for a dump to be readredirect(_Env,_Input) ->    call(redirect).%%====================================================================%% Server functions%%====================================================================%%--------------------------------------------------------------------%% Function: init/1%% Description: Initiates the server%% Returns: {ok, State}          |%%          {ok, State, Timeout} |%%          ignore               |%%          {stop, Reason}%%--------------------------------------------------------------------init([]) ->    ets:new(cdv_menu_table,[set,named_table,{keypos,#menu_item.index},public]),    ets:new(cdv_dump_index_table,[bag,named_table,public]),    {ok, #state{}}.%%--------------------------------------------------------------------%% Function: handle_call/3%% Description: Handling call messages%% Returns: {reply, Reply, State}          |%%          {reply, Reply, State, Timeout} |%%          {noreply, State}               |%%          {noreply, State, Timeout}      |%%          {stop, Reason, Reply, State}   | (terminate/2 is called)%%          {stop, Reason, State}            (terminate/2 is called)%%--------------------------------------------------------------------handle_call(start_page,_From,State=#state{file=undefined,bg_status=undefined})->    Reply = crashdump_viewer_html:welcome(),    {reply,Reply,State};handle_call(start_page, _From, State=#state{file=undefined,bg_status={done,Page}}) ->    {reply,Page,State};handle_call(start_page, _From, State=#state{file=undefined,bg_status=Status}) ->    Reply = crashdump_viewer_html:redirect(Status),    {reply,Reply,State};handle_call(start_page, _From, State) ->    Reply = crashdump_viewer_html:start_page(),    {reply,Reply,State};handle_call({read_file,Input}, _From, _State) ->    {ok,File0} = get_value("path",httpd:parse_query(Input)),    File = 	case File0 of	    [$"|FileAndSome] ->		%% Opera adds \"\" around the filename!		[$"|Elif] = lists:reverse(FileAndSome),		lists:reverse(Elif);	    _ ->		File0	end,    spawn_link(fun() -> read_file(File) end),    Status = background_status(reading,File),    Reply = crashdump_viewer_html:redirect(Status),    {reply, Reply, #state{bg_status=Status}};handle_call({translate,Input}, _From, State=#state{file=File}) ->    {ok,TranslatedFile} = get_value("path",httpd:parse_query(Input)),    spawn_link(fun() -> do_translate(File,TranslatedFile) end),    Status = background_status(translating,File),    Reply = crashdump_viewer_html:redirect(Status),    {reply, Reply, State#state{bg_status=Status}};handle_call(redirect,_From, State=#state{bg_status={done,Page}}) ->    {reply, Page, State#state{bg_status=undefined}};   handle_call(redirect,_From, State=#state{bg_status=Status}) ->    Reply = crashdump_viewer_html:redirect(Status),    {reply, Reply, State};handle_call(filename_frame,_From,State=#state{file=File}) ->    Reply = crashdump_viewer_html:filename_frame(File),    {reply,Reply,State};handle_call(initial_info_frame,_From,State=#state{file=File}) ->    GenInfo = general_info(File),    NumAtoms = GenInfo#general_info.num_atoms,    {WS,SH} = parse_vsn_str(GenInfo#general_info.system_vsn,4,false),    Reply = crashdump_viewer_html:general_info(GenInfo),    {reply,Reply,State#state{shared_heap=SH,wordsize=WS,num_atoms=NumAtoms}};handle_call({toggle,Input},_From,State) ->    {ok,Index} = get_value("index",httpd:parse_query(Input)),    do_toggle(list_to_integer(Index)),    Reply = crashdump_viewer_html:menu_frame(),    {reply,Reply,State};handle_call({expand,Input},_From,State=#state{file=File}) ->    [{"pos",Pos},{"size",Size},{"what",What},{"truncated",Truncated}] = 	httpd:parse_query(Input),    Expanded = get_expanded(File,list_to_integer(Pos),list_to_integer(Size)),    TruncText = if Truncated=="true" -> "WARNING: This term is truncated!\n\n";		   true -> ""		end,    Reply = 	case {Truncated,What} of	    {_,"LastCalls"} ->		LastCalls = replace_all($ ,$\n,Expanded,[]),

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?