diff options
author | Siri Hansen <siri@erlang.org> | 2011-02-21 10:02:16 +0100 |
---|---|---|
committer | Siri Hansen <siri@erlang.org> | 2011-02-28 17:17:41 +0100 |
commit | 38fc7a0eb46167a27ebac41452ecd1fb0de8803c (patch) | |
tree | ceae7c7ba8b6314e0e5ce8c72ebf3c107e76a414 /lib/observer/src/crashdump_viewer_html.erl | |
parent | e170c7f2ce8e0ac2cd45c922afc138305ca34b79 (diff) | |
download | otp-38fc7a0eb46167a27ebac41452ecd1fb0de8803c.tar.gz otp-38fc7a0eb46167a27ebac41452ecd1fb0de8803c.tar.bz2 otp-38fc7a0eb46167a27ebac41452ecd1fb0de8803c.zip |
Fix slow parsing of crashdumps
This is a first attempt at fixing the problem described in seq11783 -
crashdump_viewer is very slow at parsing big crashdumps. To open the
first page for a dump of 17M takes about 2 minutes and a dump of 280M
takes 1.5-2 hours.
The main problmem is that the cdv_dump_index_table, which holds all
tags read from the dump, is a bag. Profiling shows that ~95% of the
time is spent in ets:insert. The table is now changed to an
ordered_set.
A second problem occured when a page with many table rows was
opened. These pages were sent to inet in one chunk, causing both
crashdump_viewer_server and the inets (mod_esi) process to grow very
much in memory usage. To overcome this, the pages are now sent to
inets in chunks of 1000 rows, and the data is coverted to binaries to
avoid data copying between the two processes.
Also, some new information in the crashdump was not recognized by the
crashdump_viewer. This has been fixed.
Diffstat (limited to 'lib/observer/src/crashdump_viewer_html.erl')
-rw-r--r-- | lib/observer/src/crashdump_viewer_html.erl | 584 |
1 files changed, 282 insertions, 302 deletions
diff --git a/lib/observer/src/crashdump_viewer_html.erl b/lib/observer/src/crashdump_viewer_html.erl index 5e7bbf62a0..0d70c9b86f 100644 --- a/lib/observer/src/crashdump_viewer_html.erl +++ b/lib/observer/src/crashdump_viewer_html.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2011. All Rights Reserved. %% %% 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 @@ -32,25 +32,23 @@ general_info/1, pretty_info_page/2, info_page/2, - procs_summary/4, proc_details/4, expanded_memory/2, expanded_binary/1, - next/2, - ports/3, - timers/3, - ets_tables/4, + port/3, + internal_ets_tables/2, nods/2, - loaded_mods/2, loaded_mod_details/2, - funs/2, - atoms/3, + atoms/4, + atoms_chunk/2, memory/2, allocated_areas/2, allocator_info/2, hash_tables/2, index_tables/2, - error/2]). + error/2, + chunk_page/5, + chunk/3]). -include("crashdump_viewer.hrl"). @@ -235,6 +233,8 @@ general_info_body(Heading,GenInfo) -> td(GenInfo#general_info.num_procs)]), tr([th("ALIGN=left BGCOLOR=\"#8899AA\"","ETS tables"), td(GenInfo#general_info.num_ets)]), + tr([th("ALIGN=left BGCOLOR=\"#8899AA\"","Timers"), + td(GenInfo#general_info.num_timers)]), tr([th("ALIGN=left BGCOLOR=\"#8899AA\"","Funs"), td(GenInfo#general_info.num_fun)])]), case GenInfo#general_info.instr_info of @@ -295,60 +295,6 @@ pretty_info_body(Heading,Info) -> pre(pretty_format(Info))]. %%%----------------------------------------------------------------- -%%% Make table with summary of process information -procs_summary(Sorted,ProcsSummary,TW,SharedHeap) -> - Heading = "Process Information", - header(Heading, - body( - procs_summary_body(Heading,ProcsSummary,TW,Sorted,SharedHeap))). - -procs_summary_body(Heading,[],TW,_Sorted,_SharedHeap) -> - [h1(Heading), - warn(TW), - "No processes were found\n"]; -procs_summary_body(Heading,ProcsSummary,TW,Sorted,SharedHeap) -> - MemHeading = - if SharedHeap -> - "Stack"; - true -> - "Stack+heap" - end, - - [heading(Heading,"processes"), - warn(TW), - table( - "BORDER=4 CELLPADDING=4", - [tr( - [summary_table_head("pid","Pid",Sorted), - summary_table_head("name_func","Name/Spawned as",Sorted), - summary_table_head("state","State",Sorted), - summary_table_head("reds","Reductions",Sorted), - summary_table_head("mem",MemHeading,Sorted), - summary_table_head("msg_q_len","MsgQ Length",Sorted)]) | - lists:map(fun(Proc) -> procs_summary_table(Proc) end,ProcsSummary)])]. - -summary_table_head(Sorted,Text,Sorted) -> - %% Mark the sorted column (bigger and italic) - th(font("SIZE=\"+1\"",em(href("./sort_procs?sort="++Sorted,Text)))); -summary_table_head(SortOn,Text,_Sorted) -> - th(href("./sort_procs?sort="++SortOn,Text)). - -procs_summary_table(Proc) -> - #proc{pid=Pid,name=Name,state=State, - reds=Reds,stack_heap=Mem0,msg_q_len=MsgQLen}=Proc, - Mem = case Mem0 of - -1 -> "unknown"; - _ -> integer_to_list(Mem0) - end, - tr( - [td(href(["./proc_details?pid=",Pid],Pid)), - td(Name), - td(State), - td("ALIGN=right",integer_to_list(Reds)), - td("ALIGN=right",Mem), - td("ALIGN=right",integer_to_list(MsgQLen))]). - -%%%----------------------------------------------------------------- %%% Print details for one process proc_details(Pid,Proc,TW,SharedHeap) -> Script = @@ -594,83 +540,33 @@ expanded_binary_body(Heading,Bin) -> href("javascript:history.go(-1)","BACK")]. %%%----------------------------------------------------------------- -%%% Print table of ports -ports(Heading,Ports,TW) -> - header(Heading,body(ports_body(Heading,Ports,TW))). +%%% Print info for one port +port(Heading,Port,TW) -> + header(Heading,body(port_body(Heading,Port,TW))). -ports_body(Heading,[],TW) -> - [h1(Heading), - warn(TW), - "No ports were found\n"]; -ports_body(Heading,Ports,TW) -> +port_body(Heading,Port,TW) -> [heading(Heading,"ports"), warn(TW), table( "BORDER=4 CELLPADDING=4", - [tr( - [th("Id"), - th("Slot"), - th("Connected"), - th("Links"), - th("Controls")]) | - lists:map(fun(Port) -> ports_table(Port) end, Ports)])]. + [tr([th(Head) || Head <- port_table_head()]), ports_table(Port)])]. -ports_table(Port) -> - #port{id=Id,slot=Slot,connected=Connected,links=Links, - controls=Controls}=Port, - tr( - [td(Id), - td("ALIGHT=right",Slot), - td(href_proc_port(Connected)), - td(href_proc_port(Links)), - td(Controls)]). - %%%----------------------------------------------------------------- -%%% Print table of ETS tables -ets_tables(Heading,EtsTables,InternalEts,TW) -> - header(Heading,body(ets_tables_body(Heading,EtsTables,InternalEts,TW))). +%%% Print table of internal ETS tables +internal_ets_tables(InternalEts,TW) -> + Heading = "Internal ETS tables", + header(Heading,body(internal_ets_tables_body(Heading,InternalEts,TW))). -ets_tables_body(Heading,[],InternalEts,TW) -> +internal_ets_tables_body(Heading,[],TW) -> [h1(Heading), warn(TW), - "No ETS tables were found\n" | - internal_ets_tables_table(InternalEts)]; -ets_tables_body(Heading,EtsTables,InternalEts,TW) -> - [heading(Heading,"ets_tables"), + "No internal ETS tables were found\n"]; +internal_ets_tables_body(Heading,InternalEts,TW) -> + [heading(Heading,"internal_ets_tables"), warn(TW), table( "BORDER=4 CELLPADDING=4", [tr( - [th("Owner"), - th("Slot"), - th("Id"), - th("Name"), - th("Type"), - th("Buckets"), - th("Objects"), - th("Memory (bytes)")]) | - lists:map(fun(EtsTable) -> ets_tables_table(EtsTable) end, - EtsTables)]) | - internal_ets_tables_table(InternalEts)]. - -ets_tables_table(EtsTable) -> - #ets_table{pid=Pid,slot=Slot,id=Id,name=Name,type=Type, - buckets=Buckets,size=Size,memory=Memory} = EtsTable, - tr( - [td(href_proc_port(Pid)), - td(Slot), - td(Id), - td(Name), - td(Type), - td("ALIGN=right",Buckets), - td("ALIGN=right",Size), - td("ALIGN=right",Memory)]). - -internal_ets_tables_table(InternalEtsTables) -> - [h2("Internal ETS tables"), - table( - "BORDER=4 CELLPADDING=4", - [tr( [th("Description"), th("Id"), th("Name"), @@ -681,7 +577,7 @@ internal_ets_tables_table(InternalEtsTables) -> lists:map(fun(InternalEtsTable) -> internal_ets_tables_table1(InternalEtsTable) end, - InternalEtsTables)])]. + InternalEts)])]. internal_ets_tables_table1({Descr,InternalEtsTable}) -> #ets_table{id=Id,name=Name,type=Type,buckets=Buckets, @@ -696,33 +592,6 @@ internal_ets_tables_table1({Descr,InternalEtsTable}) -> td("ALIGN=right",Memory)]). %%%----------------------------------------------------------------- -%%% Print table of timers -timers(Heading,Timers,TW) -> - header(Heading,body(timers_body(Heading,Timers,TW))). - -timers_body(Heading,[],TW) -> - [h1(Heading), - warn(TW), - "No timers were found\n"]; -timers_body(Heading,Timers,TW) -> - [heading(Heading,"timers"), - warn(TW), - table( - "BORDER=4 CELLPADDING=4", - [tr( - [th("Owner"), - th("Message"), - th("Time left")]) | - lists:map(fun(Timer) -> timers_table(Timer) end, Timers)])]. - -timers_table(Timer) -> - #timer{pid=Pid,msg=Msg,time=Time}=Timer, - tr( - [td(href_proc_port(Pid)), - td(Msg), - td("ALIGN=right",Time)]). - -%%%----------------------------------------------------------------- %%% Print table of nodes in distribution nods(Nods,TW) -> header("Distribution Information",body(nodes_body(Nods,TW))). @@ -826,33 +695,6 @@ format_extra_info(Error) -> ?space -> ""; _ -> font("COLOR=\"#FF0000\"",["ERROR: ",Error,"\n"]) end. -%%%----------------------------------------------------------------- -%%% Print loaded modules information -loaded_mods({CC,OC,LM},TW) -> - Heading = "Loaded Modules Information", - header(Heading,body(loaded_mods_body(Heading,CC,OC,LM,TW))). - -loaded_mods_body(Heading,"unknown","unknown",[],TW) -> - [h1(Heading), - warn(TW), - "No loaded modules information was found\n"]; -loaded_mods_body(Heading,CC,OC,LM,TW) -> - [heading(Heading,"loaded_modules"), - warn(TW), - p([b("Current code: "),CC," bytes",br(), - b("Old code: "),OC," bytes"]), - table( - "BORDER=4 CELLPADDING=4", - [tr([th("Module"), - th("Current size (bytes)"), - th("Old size (bytes)")]) | - lists:map(fun(Mod) -> loaded_mods_table(Mod) end,LM)])]. - -loaded_mods_table(#loaded_mod{mod=Mod,current_size=CS,old_size=OS}) -> - tr([td(href(["loaded_mod_details?mod=",Mod],Mod)), - td("ALIGN=right",CS), - td("ALIGN=right",OS)]). - %%%----------------------------------------------------------------- %%% Print detailed information about one module @@ -882,107 +724,32 @@ loaded_mod_details_body(ModInfo,TW) -> %%%----------------------------------------------------------------- -%%% Print table of funs -funs(Funs,TW) -> - Heading = "Fun Information", - header(Heading,body(funs_body(Heading,Funs,TW))). - -funs_body(Heading,[],TW) -> - [h1(Heading), - warn(TW), - "No Fun information was found\n"]; -funs_body(Heading,Funs,TW) -> - [heading(Heading,"funs"), - warn(TW), - table( - "BORDER=4 CELLPADDING=4", - [tr( - [th("Module"), - th("Uniq"), - th("Index"), - th("Address"), - th("Native_address"), - th("Refc")]) | - lists:map(fun(Fun) -> funs_table(Fun) end, Funs)])]. - -funs_table(Fu) -> - #fu{module=Module,uniq=Uniq,index=Index,address=Address, - native_address=NativeAddress,refc=Refc}=Fu, - tr( - [td(Module), - td("ALIGN=right",Uniq), - td("ALIGN=right",Index), - td(Address), - td(NativeAddress), - td("ALIGN=right",Refc)]). - -%%%----------------------------------------------------------------- %%% Print atoms -atoms(Atoms,Num,TW) -> +atoms(SessionId,TW,Num,FirstChunk) -> Heading = "Atoms", - header(Heading,body(atoms_body(Heading,Atoms,Num,TW))). - -atoms_body(Heading,[],Num,TW) -> - [h1(Heading), - warn(TW), - "No atoms were found in log",br(), - "Total number of atoms in node was ", Num, br()]; -atoms_body(Heading,Atoms,Num,TW) -> - [heading(Heading,"atoms"), - warn(TW), - "Total number of atoms in node was ", Num, - br(), - "The last created atom is shown first", - br(),br() | - n_first(Atoms)]. - -n_first({n_lines,Start,N,What,Lines,Pos}) -> - NextHref = next_href(N,What,Pos,Start), - [What," number ",integer_to_list(Start),"-",integer_to_list(Start+N-1), - br(), - NextHref, - pre(Lines), - NextHref]; -n_first({n_lines,_Start,_N,_What,Lines}) -> - [pre(Lines)]. - -%%%----------------------------------------------------------------- -%%% Print next N lines of "something" -next(NLines,TW) -> - header(element(4,NLines),body(next_body(NLines,TW))). - -next_body({n_lines,Start,N,What,Lines,Pos},TW) -> - PrefHref = prev_href(), - NextHref = next_href(N,What,Pos,Start), - [warn(TW), - What," number ",integer_to_list(Start),"-",integer_to_list(Start+N-1), - br(), - PrefHref, - ?space, - NextHref, - pre(Lines), - PrefHref, - ?space, - NextHref]; -next_body({n_lines,Start,N,What,Lines},TW) -> - PrefHref = prev_href(), - [warn(TW), - What," number ",integer_to_list(Start),"-",integer_to_list(Start+N-1), - br(), - PrefHref, - pre(Lines), - PrefHref]. - - -prev_href() -> - href("javascript:history.back()",["Previous"]). - -next_href(N,What,Pos,Start) -> - href(["./next?pos=",integer_to_list(Pos), - "&num=",integer_to_list(N), - "&start=",integer_to_list(Start+N), - "&what=",What], - "Next"). + case FirstChunk of + done -> + deliver_first(SessionId,[h1(Heading), + warn(TW), + "No atoms were found in log",br(), + "Total number of atoms in node was ", Num, + br()]); + _ -> + deliver_first(SessionId,[start_html_page(Heading), + heading(Heading,"atoms"), + warn(TW), + "Total number of atoms in node was ", Num, + br(), + "The last created atom is shown first", + br(), + start_pre()]), + atoms_chunk(SessionId,FirstChunk) + end. + +atoms_chunk(SessionId,done) -> + deliver(SessionId,[stop_pre(),stop_html_page()]); +atoms_chunk(SessionId,Atoms) -> + deliver(SessionId,Atoms). %%%----------------------------------------------------------------- %%% Print memory information @@ -1120,52 +887,93 @@ index_tables_body(Heading,IndexTables,TW) -> th("Size"), th("Limit"), th("Used"), - th("Rate")]) | + th("Rate"), + th("Entries")]) | lists:map(fun(IndexTable) -> index_tables_table(IndexTable) end, IndexTables)])]. index_tables_table(IndexTable) -> - #index_table{name=Name,size=Size,limit=Limit,used=Used,rate=Rate} = - IndexTable, + #index_table{name=Name,size=Size,limit=Limit,used=Used, + rate=Rate,entries=Entries} = IndexTable, tr( [td(Name), td("ALIGN=right",Size), td("ALIGN=right",Limit), td("ALIGN=right",Used), - td("ALIGN=right",Rate)]). + td("ALIGN=right",Rate), + td("ALIGN=right",Entries)]). %%%----------------------------------------------------------------- %%% Internal library +start_html_page(Title) -> + [only_http_header(), + start_html(), + only_html_header(Title), + start_html_body()]. + +stop_html_page() -> + [stop_html_body(), + stop_html()]. + +only_http_header() -> + ["Pragma:no-cache\r\n", + "Content-type: text/html\r\n\r\n"]. + +only_html_header(Title) -> + only_html_header(Title,""). +only_html_header(Title,JavaScript) -> + ["<HEAD>\n", + "<TITLE>", Title, "</TITLE>\n", + JavaScript, + "</HEAD>\n"]. + +start_html() -> + "<HTML>\n". +stop_html() -> + "</HTML>". +start_html_body() -> + "<BODY BGCOLOR=\"#FFFFFF\">\n". +stop_html_body() -> + "</BODY>\n". + header(Body) -> header("","",Body). header(Title,Body) -> header(Title,"",Body). header(Title,JavaScript,Body) -> - ["Pragma:no-cache\r\n", - "Content-type: text/html\r\n\r\n", + [only_http_header(), html_header(Title,JavaScript,Body)]. html_header(Title,JavaScript,Body) -> - ["<HTML>\n", - "<HEAD>\n", - "<TITLE>", Title, "</TITLE>\n", - JavaScript, - "</HEAD>\n", + [start_html(), + only_html_header(Title,JavaScript), Body, - "</HTML>"]. + stop_html()]. body(Text) -> - ["<BODY BGCOLOR=\"#FFFFFF\">\n", + [start_html_body(), Text, - "<\BODY>\n"]. + stop_html_body()]. frameset(Args,Frames) -> ["<FRAMESET ",Args,">\n", Frames, "\n</FRAMESET>\n"]. frame(Args) -> ["<FRAME ",Args, ">\n"]. +start_visible_table() -> + start_table("BORDER=\"4\" CELLPADDING=\"4\" WIDTH=\"100%\""). +% start_table("BORDER=\"4\" CELLPADDING=\"4\""). +start_visible_table(ColTitles) -> + [start_visible_table(), + tr([th(ColTitle) || ColTitle <- ColTitles])]. + +start_table(Args) -> + ["<TABLE ", Args, ">\n"]. +stop_table() -> + "</TABLE>\n". + table(Args,Text) -> - ["<TABLE ", Args, ">\n", Text, "\n</TABLE>\n"]. + [start_table(Args), Text, stop_table()]. tr(Text) -> ["<TR>\n", Text, "\n</TR>\n"]. tr(Args,Text) -> @@ -1183,8 +991,12 @@ b(Text) -> ["<B>",Text,"</B>"]. em(Text) -> ["<EM>",Text,"</EM>\n"]. +start_pre() -> + "<PRE>". +stop_pre() -> + "</PRE>". pre(Text) -> - ["<PRE>",Text,"</PRE>"]. + [start_pre(),Text,stop_pre()]. href(Link,Text) -> ["<A HREF=\"",Link,"\">",Text,"</A>"]. href(Args,Link,Text) -> @@ -1199,8 +1011,6 @@ input(Args) -> ["<INPUT ", Args, ">\n"]. h1(Text) -> ["<H1>",Text,"</H1>\n"]. -h2(Text) -> - ["<H2>",Text,"</H2>\n"]. font(Args,Text) -> ["<FONT ",Args,">\n",Text,"\n</FONT>\n"]. p(Text) -> @@ -1223,7 +1033,7 @@ href_proc_port([$#,$F,$u,$n,$<|T],Acc) -> href_proc_port([$#,$P,$o,$r,$t,$<|T],Acc) -> {[$#|Port]=HashPort,Rest} = to_gt(T,[$;,$t,$l,$&,$t,$r,$o,$P,$#]), href_proc_port(Rest,[href("TARGET=\"main\"", - ["./ports?port=",Port],HashPort)|Acc]); + ["./port?port=",Port],HashPort)|Acc]); href_proc_port([$<,$<|T],Acc) -> %% No links to binaries href_proc_port(T,[$;,$t,$l,$&,$;,$t,$l,$&|Acc]); @@ -1243,7 +1053,7 @@ href_proc_port([$",$#,$C,$D,$V,$P,$o,$r,$t,$<|T],Acc) -> %% Port written by crashdump_viewer:parse_term(...) {[$#|Port]=HashPort,[$"|Rest]} = to_gt(T,[$;,$t,$l,$&,$t,$r,$o,$P,$#]), href_proc_port(Rest,[href("TARGET=\"main\"", - ["./ports?port=",Port],HashPort)|Acc]); + ["./port?port=",Port],HashPort)|Acc]); href_proc_port([$",$#,$C,$D,$V,$P,$i,$d,$<|T],Acc) -> %% Pid written by crashdump_viewer:parse_term(...) {Pid,[$"|Rest]} = to_gt(T,[$;,$t,$l,$&]), @@ -1422,7 +1232,7 @@ replace_insrt("'trsni$'"++Rest,[H|T],Acc) -> % the list is reversed here! "<" ++ _Pid -> href("TARGET=\"main\"",["./proc_details?pid=",H],H); "#Port<" ++ Port -> - href("TARGET=\"main\"",["./ports?port=","Port<"++Port],H); + href("TARGET=\"main\"",["./port?port=","Port<"++Port],H); "#" ++ _other -> H end, @@ -1431,3 +1241,173 @@ replace_insrt([H|T],Insrt,Acc) -> replace_insrt(T,Insrt,[H|Acc]); replace_insrt([],[],Acc) -> Acc. + +%%%----------------------------------------------------------------- +%%% Create a page with one table by delivering chunk by chunk to +%%% inets. crashdump_viewer first calls chunk_page/5 once, then +%%% chunk/3 multiple times until all data is delivered. +chunk_page(processes,SessionId,TW,{Sorted,SharedHeap},FirstChunk) -> + Columns = procs_summary_table_head(Sorted,SharedHeap), + chunk_page(SessionId, "Process Information", TW, FirstChunk, + "processes", Columns, fun procs_summary_table/1); +chunk_page(ports,SessionId,TW,_,FirstChunk) -> + chunk_page(SessionId, "Port Information", TW, FirstChunk, + "ports", port_table_head(), fun ports_table/1); +chunk_page(ets_tables,SessionId,TW,Heading,FirstChunk) -> + Columns = ["Owner", + "Slot", + "Id", + "Name", + "Type", + "Buckets", + "Objects", + "Memory (bytes)"], + chunk_page(SessionId, Heading, TW, FirstChunk, + "ets_tables", Columns, fun ets_tables_table/1); +chunk_page(timers,SessionId,TW,Heading,FirstChunk) -> + chunk_page(SessionId, Heading, TW, FirstChunk, "timers", + ["Owner","Message","Time left"], fun timers_table/1); +chunk_page(loaded_mods,SessionId,TW,{CC,OC},FirstChunk) -> + TotalsInfo = p([b("Current code: "),CC," bytes",br(), + b("Old code: "),OC," bytes"]), + Columns = ["Module","Current size (bytes)","Old size (bytes)"], + chunk_page(SessionId, "Loaded Modules Information", TW, FirstChunk, + "loaded_modules", TotalsInfo,Columns, fun loaded_mods_table/1); +chunk_page(funs,SessionId, TW, _, FirstChunk) -> + Columns = ["Module", + "Uniq", + "Index", + "Address", + "Native_address", + "Refc"], + chunk_page(SessionId, "Fun Information", TW, FirstChunk, + "funs", Columns, fun funs_table/1). + +chunk_page(SessionId,Heading,TW,FirstChunk,Type,TableColumns,TableFun) -> + chunk_page(SessionId,Heading,TW,FirstChunk,Type,[],TableColumns,TableFun). +chunk_page(SessionId,Heading,TW,done,Type,_TotalsInfo,_TableColumns,_TableFun) -> + no_info_found(SessionId,Heading,TW,Type); +chunk_page(SessionId,Heading,TW,FirstChunk,Type,TotalsInfo,TableColumns,TableFun) -> + deliver_first(SessionId,[start_html_page(Heading), + heading(Heading,Type), + warn(TW), + TotalsInfo, + start_visible_table(TableColumns)]), + chunk(SessionId,FirstChunk,TableFun), + TableFun. + +no_info_found(SessionId, Heading, TW, Type) -> + Info = ["No ", Type, " were found\n"], + deliver_first(SessionId,[start_html_page(Heading), + h1(Heading), + warn(TW), + Info, + stop_html_page()]). + +chunk(SessionId, done, _TableFun) -> + deliver(SessionId,[stop_table(),stop_html_page()]); +chunk(SessionId, Items, TableFun) -> + deliver(SessionId, [lists:map(TableFun, Items), + stop_table(), %! Will produce an empty table at the end + start_visible_table()]). % of the page :( + +%%%----------------------------------------------------------------- +%%% Deliver part of a page to inets +%%% The first part, which includes the HTTP header, must always be +%%% delivered as a string (i.e. no binaries). The rest of the page is +%%% better delivered as binaries in order to avoid data copying. +deliver_first(SessionId,String) -> + mod_esi:deliver(SessionId,String). +deliver(SessionId,IoList) -> + mod_esi:deliver(SessionId,[list_to_binary(IoList)]). + + +%%%----------------------------------------------------------------- +%%% Page specific stuff for chunk pages +procs_summary_table_head(Sorted,SharedHeap) -> + MemHeading = + if SharedHeap -> + "Stack"; + true -> + "Stack+heap" + end, + [procs_summary_table_head("pid","Pid",Sorted), + procs_summary_table_head("name_func","Name/Spawned as",Sorted), + procs_summary_table_head("state","State",Sorted), + procs_summary_table_head("reds","Reductions",Sorted), + procs_summary_table_head("mem",MemHeading,Sorted), + procs_summary_table_head("msg_q_len","MsgQ Length",Sorted)]. + +procs_summary_table_head(_,Text,no_sort) -> + Text; +procs_summary_table_head(Sorted,Text,Sorted) -> + %% Mark the sorted column (bigger and italic) + font("SIZE=\"+1\"",em(href("./sort_procs?sort="++Sorted,Text))); +procs_summary_table_head(SortOn,Text,_Sorted) -> + href("./sort_procs?sort="++SortOn,Text). + +procs_summary_table(Proc) -> + #proc{pid=Pid,name=Name,state=State, + reds=Reds,stack_heap=Mem0,msg_q_len=MsgQLen}=Proc, + Mem = case Mem0 of + -1 -> "unknown"; + _ -> integer_to_list(Mem0) + end, + tr( + [td(href(["./proc_details?pid=",Pid],Pid)), + td(Name), + td(State), + td("ALIGN=right",integer_to_list(Reds)), + td("ALIGN=right",Mem), + td("ALIGN=right",integer_to_list(MsgQLen))]). + +port_table_head() -> + ["Id","Slot","Connected","Links","Name","Monitors","Controls"]. + +ports_table(Port) -> + #port{id=Id,slot=Slot,connected=Connected,links=Links,name=Name, + monitors=Monitors,controls=Controls}=Port, + tr( + [td(Id), + td("ALIGN=right",Slot), + td(href_proc_port(Connected)), + td(href_proc_port(Links)), + td(Name), + td(href_proc_port(Monitors)), + td(Controls)]). + +ets_tables_table(EtsTable) -> + #ets_table{pid=Pid,slot=Slot,id=Id,name=Name,type=Type, + buckets=Buckets,size=Size,memory=Memory} = EtsTable, + tr( + [td(href_proc_port(Pid)), + td(Slot), + td(Id), + td(Name), + td(Type), + td("ALIGN=right",Buckets), + td("ALIGN=right",Size), + td("ALIGN=right",Memory)]). + +timers_table(Timer) -> + #timer{pid=Pid,msg=Msg,time=Time}=Timer, + tr( + [td(href_proc_port(Pid)), + td(Msg), + td("ALIGN=right",Time)]). + +loaded_mods_table(#loaded_mod{mod=Mod,current_size=CS,old_size=OS}) -> + tr([td(href(["loaded_mod_details?mod=",Mod],Mod)), + td("ALIGN=right",CS), + td("ALIGN=right",OS)]). + +funs_table(Fu) -> + #fu{module=Module,uniq=Uniq,index=Index,address=Address, + native_address=NativeAddress,refc=Refc}=Fu, + tr( + [td(Module), + td("ALIGN=right",Uniq), + td("ALIGN=right",Index), + td(Address), + td(NativeAddress), + td("ALIGN=right",Refc)]). |