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