113
117
% this, it must be explicitly expanded.
114
118
-define(max_display_binary_size,50). % max size of a binary that will be
115
119
% directly displayed.
117
-define(initial_proc_record(Pid),
119
%% msg_q_len, reds and stack_heap are integers because it must
120
%% be possible to sort on them. All other fields are strings
121
msg_q_len=0,reds=0,stack_heap=0,
122
%% for old dumps start_time, parent and number of heap frament
124
start_time="unknown",
126
num_heap_frag="unknown",
127
%% current_func can be both "current function" and
128
%% "last scheduled in for"
129
current_func={"Current Function",?space},
130
%% stack_dump, message queue and dictionaries should only be
131
%% displayed as a link to "Expand" (if dump is from OTP R9B
120
-define(max_sort_process_num,10000). % Max number of processes that allows
121
% sorting. If more than this number of
122
% processes exist, they will be displayed
123
% in the order they are found in the log.
124
-define(items_chunk_size,?max_sort_process_num). % Number of items per chunk
125
% when page of many items
126
% is displayed, e.g. processes,
129
% ?max_sort_process_num!
131
%% All possible tags - use macros in order to avoid misspelling in the code
132
-define(allocated_areas,allocated_areas).
133
-define(allocator,allocator).
134
-define(atoms,atoms).
135
-define(binary,binary).
136
-define(debug_proc_dictionary,debug_proc_dictionary).
138
-define(erl_crash_dump,erl_crash_dump).
141
-define(hash_table,hash_table).
142
-define(hidden_node,hidden_node).
143
-define(index_table,index_table).
144
-define(instr_data,instr_data).
145
-define(internal_ets,internal_ets).
146
-define(loaded_modules,loaded_modules).
147
-define(memory,memory).
149
-define(no_distribution,no_distribution).
151
-define(not_connected,not_connected).
152
-define(num_atoms,num_atoms).
153
-define(old_instr_data,old_instr_data).
156
-define(proc_dictionary,proc_dictionary).
157
-define(proc_heap,proc_heap).
158
-define(proc_messages,proc_messages).
159
-define(proc_stack,proc_stack).
160
-define(timer,timer).
161
-define(visible_node,visible_node).
135
164
-record(state,{file,procs_summary,sorted,shared_heap=false,
136
165
wordsize=4,num_atoms="unknown",binaries,bg_status}).
179
208
%%%-----------------------------------------------------------------
209
%%% Start crashdump_viewer via the cdv script located in
210
%%% $OBSERVER_PRIV_DIR/bin
213
script_start([File]) ->
216
{win32,_} -> iexplore;
219
script_start([File,DefaultBrowser]);
220
script_start([FileAtom,Browser]) ->
221
File = atom_to_list(FileAtom),
222
case filelib:is_regular(File) of
224
io:format("Starting crashdump_viewer...\n"),
226
io:format("Reading crashdump..."),
230
start_browser(Browser);
232
io:format("cdv error: the given file does not exist\n"),
236
start_browser(Browser) ->
237
PortStr = integer_to_list(gen_server:call(web_tool,get_port)),
238
Url = "http://localhost:" ++ PortStr ++ ?START_PAGE,
239
{OSType,_} = os:type(),
243
iexplore when OSType == win32->
244
io:format("Starting internet explorer...\n"),
245
{ok,R} = win32reg:open(""),
246
Key="\\local_machine\\SOFTWARE\\Microsoft\\IE Setup\\Setup",
247
win32reg:change_key(R,Key),
248
{ok,Val} = win32reg:value(R,"Path"),
249
IExplore=filename:join(win32reg:expand(Val),"iexplore.exe"),
250
os:cmd("\"" ++ IExplore ++ "\" " ++ Url);
251
_ when OSType == win32 ->
252
io:format("Starting ~w...\n",[Browser]),
253
os:cmd("\"" ++ atom_to_list(Browser) ++ "\" " ++ Url);
254
B when B==firefox; B==mozilla ->
255
io:format("Sending URL to ~w...",[Browser]),
256
BStr = atom_to_list(Browser),
257
SendCmd = BStr ++ " -raise -remote \'openUrl(" ++ Url ++ ")\'",
258
Port = open_port({spawn,SendCmd},[exit_status]),
260
{Port,{exit_status,0}} ->
262
{Port,{exit_status,_Error}} ->
263
io:format(" not running, starting ~w...\n",[Browser]),
264
os:cmd(BStr ++ " " ++ Url)
266
io:format(" failed, starting ~w...\n",[Browser]),
267
erlang:port_close(Port),
268
os:cmd(BStr ++ " " ++ Url)
271
io:format("Starting ~w...\n",[Browser]),
272
os:cmd(atom_to_list(Browser) ++ " " ++ Url)
278
"\nusage: cdv file [ browser ]\n"
279
"\tThe \'file\' must be an existing erlang crash dump.\n"
280
"\tDefault browser is \'iexplore\' (Internet Explorer) on Windows\n"
281
"\tor else \'firefox\'.\n",
287
%%%-----------------------------------------------------------------
180
288
%%% Return config data used by webtool
182
290
Dir = filename:join(code:priv_dir(observer),"crashdump_viewer"),
451
561
Reply=crashdump_viewer_html:expanded_binary(io_lib:format("~p",[Bin])),
452
562
{reply,Reply,State};
453
handle_call({next,Input},_From,State=#state{file=File}) ->
454
[{"pos",Pos},{"num",N},{"start",Start},{"what",What}] =
455
httpd:parse_query(Input),
456
Tags = related_tags(What),
457
TW = truncated_warning(Tags),
458
Next = get_next(File,list_to_integer(Pos),list_to_integer(N),
459
list_to_integer(Start),What),
460
Reply = crashdump_viewer_html:next(Next,TW),
462
563
handle_call(general_info,_From,State=#state{file=File}) ->
463
564
GenInfo=general_info(File),
464
565
Reply = crashdump_viewer_html:general_info(GenInfo),
465
566
{reply,Reply,State};
466
handle_call(procs_summary,_From,State=#state{file=File,shared_heap=SH}) ->
468
case State#state.procs_summary of
469
undefined -> procs_summary(File);
472
TW = truncated_warning(["=proc"]),
473
Reply = crashdump_viewer_html:procs_summary("pid",ProcsSummary,TW,SH),
474
{reply,Reply,State#state{procs_summary=ProcsSummary,sorted="pid"}};
475
handle_call({sort_procs,Input}, _From, State=#state{shared_heap=SH}) ->
567
handle_call({procs_summary,SessionId},_From,State) ->
568
TW = truncated_warning([?proc]),
569
NewState = procs_summary(SessionId,TW,"pid",State#state{sorted=undefined}),
571
handle_call({sort_procs,SessionId,Input}, _From, State) ->
476
572
{ok,Sort} = get_value("sort",httpd:parse_query(Input)),
477
{ProcsSummary,Sorted} = do_sort_procs(Sort,
478
State#state.procs_summary,
480
TW = truncated_warning(["=proc"]),
481
Reply = crashdump_viewer_html:procs_summary(Sort,ProcsSummary,TW,SH),
482
{reply,Reply,State#state{sorted=Sorted}};
573
TW = truncated_warning([?proc]),
574
NewState = procs_summary(SessionId,TW,Sort,State),
483
576
handle_call({proc_details,Input},_From,State=#state{file=File,shared_heap=SH}) ->
484
577
{ok,Pid} = get_value("pid",httpd:parse_query(Input)),
486
579
case get_proc_details(File,Pid) of
488
TW = truncated_warning([{"=proc",Pid}]),
581
TW = truncated_warning([{?proc,Pid}]),
489
582
crashdump_viewer_html:proc_details(Pid,Proc,TW,SH);
490
583
{other_node,Node} ->
491
TW = truncated_warning(["=visible_node",
584
TW = truncated_warning([?visible_node,
494
587
crashdump_viewer_html:nods(Node,TW);
496
589
crashdump_viewer_html:info_page(["Could not find process: ",
499
592
{reply, Reply, State};
500
handle_call({ports,Input},_From,State=#state{file=File}) ->
593
handle_call({port,Input},_From,State=#state{file=File}) ->
594
{ok,P} = get_value("port",httpd:parse_query(Input)),
502
case get_value("port",httpd:parse_query(Input)) of
505
case get_port(File,Id) of
507
TW = truncated_warning([{"=port",Id}]),
508
crashdump_viewer_html:ports(Id,[PortInfo],TW);
510
TW = truncated_warning(["=visible_node",
513
crashdump_viewer_html:nods(Node,TW);
515
crashdump_viewer_html:info_page(
516
["Could not find port: ",Id],?space)
518
error -> % no port identity in Input - get all ports
519
Ports=get_ports(File),
520
TW = truncated_warning(["=port"]),
521
crashdump_viewer_html:ports("Port Information",Ports,TW)
597
case get_port(File,Id) of
599
TW = truncated_warning([{?port,Id}]),
600
crashdump_viewer_html:port(Id,PortInfo,TW);
602
TW = truncated_warning([?visible_node,
605
crashdump_viewer_html:nods(Node,TW);
607
crashdump_viewer_html:info_page(
608
["Could not find port: ",Id],?space)
523
610
{reply,Reply,State};
524
handle_call({ets_tables,Input},_From,State=#state{file=File,wordsize=WS}) ->
525
{Pid,Heading,InternalEts} =
611
handle_call({ports,SessionId},_From,State=#state{file=File}) ->
612
TW = truncated_warning([?port]),
613
get_ports(SessionId,File,TW),
615
handle_call({ets_tables,SessionId,Input},_From,State=#state{file=File,wordsize=WS}) ->
526
617
case get_value("pid",httpd:parse_query(Input)) of
528
{P,["ETS Tables for Process ",P],[]};
619
{P,["ETS Tables for Process ",P]};
530
I = get_internal_ets_tables(File,WS),
531
{'_',"ETS Table Information",I}
621
{'$2',"ETS Table Information"}
533
EtsTables = get_ets_tables(File,Pid,WS),
534
TW = truncated_warning(["=ets"]),
535
Reply = crashdump_viewer_html:ets_tables(Heading,EtsTables,InternalEts,TW),
623
TW = truncated_warning([?ets]),
624
get_ets_tables(SessionId,File,Heading,TW,Pid,WS),
626
handle_call(internal_ets_tables,_From,State=#state{file=File,wordsize=WS}) ->
627
InternalEts = get_internal_ets_tables(File,WS),
628
TW = truncated_warning([?internal_ets]),
629
Reply = crashdump_viewer_html:internal_ets_tables(InternalEts,TW),
536
630
{reply,Reply,State};
537
handle_call({timers,Input},_From,State=#state{file=File}) ->
631
handle_call({timers,SessionId,Input},_From,State=#state{file=File}) ->
539
633
case get_value("pid",httpd:parse_query(Input)) of
540
634
{ok,P} -> {P,["Timers for Process ",P]};
541
error -> {'_',"Timer Information"}
635
error -> {'$2',"Timer Information"}
543
Timers=get_timers(File,Pid),
544
TW = truncated_warning(["=timer"]),
545
Reply = crashdump_viewer_html:timers(Heading,Timers,TW),
637
TW = truncated_warning([?timer]),
638
get_timers(SessionId,File,Heading,TW,Pid),
547
640
handle_call(dist_info,_From,State=#state{file=File}) ->
549
TW = truncated_warning(["=visible_node","=hidden_node","=not_connected"]),
642
TW = truncated_warning([?visible_node,?hidden_node,?not_connected]),
550
643
Reply = crashdump_viewer_html:nods(Nods,TW),
551
644
{reply,Reply,State};
552
handle_call(loaded_mods,_From,State=#state{file=File}) ->
553
LoadedMods=loaded_mods(File),
554
TW = truncated_warning(["=mod"]),
555
Reply = crashdump_viewer_html:loaded_mods(LoadedMods,TW),
645
handle_call({loaded_mods,SessionId},_From,State=#state{file=File}) ->
646
TW = truncated_warning([?mod]),
647
loaded_mods(SessionId,File,TW),
557
649
handle_call({loaded_mod_details,Input},_From,State=#state{file=File}) ->
558
650
{ok,Mod} = get_value("mod",httpd:parse_query(Input)),
559
651
ModInfo = get_loaded_mod_details(File,Mod),
560
TW = truncated_warning([{"=mod",Mod}]),
652
TW = truncated_warning([{?mod,Mod}]),
561
653
Reply = crashdump_viewer_html:loaded_mod_details(ModInfo,TW),
562
654
{reply,Reply,State};
563
handle_call(funs,_From,State=#state{file=File}) ->
565
TW = truncated_warning(["=fun"]),
566
Reply = crashdump_viewer_html:funs(Funs,TW),
568
handle_call(atoms,_From,State=#state{file=File,num_atoms=Num}) ->
570
TW = truncated_warning(["=atoms","=num_atoms"]),
571
Reply = crashdump_viewer_html:atoms(Atoms,Num,TW),
655
handle_call({funs,SessionId},_From,State=#state{file=File}) ->
656
TW = truncated_warning([?fu]),
657
funs(SessionId,File,TW),
659
handle_call({atoms,SessionId},_From,State=#state{file=File,num_atoms=Num}) ->
660
TW = truncated_warning([?atoms,?num_atoms]),
661
atoms(SessionId,File,TW,Num),
573
663
handle_call(memory,_From,State=#state{file=File}) ->
574
664
Memory=memory(File),
575
TW = truncated_warning(["=memory"]),
665
TW = truncated_warning([?memory]),
576
666
Reply = crashdump_viewer_html:memory(Memory,TW),
577
667
{reply,Reply,State};
578
668
handle_call(allocated_areas,_From,State=#state{file=File}) ->
579
669
AllocatedAreas=allocated_areas(File),
580
TW = truncated_warning(["=allocated_areas"]),
670
TW = truncated_warning([?allocated_areas]),
581
671
Reply = crashdump_viewer_html:allocated_areas(AllocatedAreas,TW),
582
672
{reply,Reply,State};
583
673
handle_call(allocator_info,_From,State=#state{file=File}) ->
584
674
SlAlloc=allocator_info(File),
585
TW = truncated_warning(["=allocator"]),
675
TW = truncated_warning([?allocator]),
586
676
Reply = crashdump_viewer_html:allocator_info(SlAlloc,TW),
587
677
{reply,Reply,State};
588
678
handle_call(hash_tables,_From,State=#state{file=File}) ->
589
679
HashTables=hash_tables(File),
590
TW = truncated_warning(["=hash_table","=index_table"]),
680
TW = truncated_warning([?hash_table,?index_table]),
591
681
Reply = crashdump_viewer_html:hash_tables(HashTables,TW),
592
682
{reply,Reply,State};
593
683
handle_call(index_tables,_From,State=#state{file=File}) ->
594
684
IndexTables=index_tables(File),
595
TW = truncated_warning(["=hash_table","=index_table"]),
685
TW = truncated_warning([?hash_table,?index_table]),
596
686
Reply = crashdump_viewer_html:index_tables(IndexTables,TW),
597
687
{reply,Reply,State}.
1107
1227
background_done({R,undefined,undefined})
1110
indexify(Fd,<<"\n=",TagAndRest/binary>>,N) ->
1111
{Tag,Id,Rest,N1} = tag(Fd,TagAndRest,N+2),
1112
ets:insert(cdv_dump_index_table,{Tag,Id,N1+1}), % +1 to get past newline
1113
put(last_tag,{Tag,Id}),
1114
indexify(Fd,Rest,N1);
1115
indexify(Fd,<<>>,N) ->
1117
{ok,Chunk} when is_binary(Chunk) ->
1118
indexify(Fd,Chunk,N);
1122
indexify(Fd,<<$\n>>,N) ->
1123
%% This clause is needed in case the chunk ends with a newline and
1124
%% the next chunk starts with a tag (i.e. "\n=....")
1126
{ok,Chunk} when is_binary(Chunk) ->
1127
indexify(Fd,<<$\n,Chunk/binary>>,N);
1131
indexify(Fd,<<_Char:8,Rest/binary>>,N) ->
1132
indexify(Fd,Rest,N+1).
1230
indexify(Fd,Bin,N) ->
1231
case binary:match(Bin,<<"\n=">>) of
1234
<<_:Pos/binary,TagAndRest/binary>> = Bin,
1235
{Tag,Id,Rest,N1} = tag(Fd,TagAndRest,N+Pos),
1236
insert_index(Tag,Id,N1+1), % +1 to get past newline
1237
put(last_tag,{Tag,Id}),
1238
indexify(Fd,Rest,N1);
1241
{ok,Chunk0} when is_binary(Chunk0) ->
1243
case binary:last(Bin) of
1245
{<<$\n,Chunk0/binary>>,N+byte_size(Bin)-1};
1247
{Chunk0,N+byte_size(Bin)}
1249
indexify(Fd,Chunk,N1);
1134
1255
tag(Fd,Bin,N) ->
1135
1256
tag(Fd,Bin,N,[],[],tag).
1136
1257
tag(_Fd,<<$\n:8,_/binary>>=Rest,N,Gat,Di,_Now) ->
1137
{[$=|lists:reverse(Gat)],lists:reverse(Di),Rest,N};
1258
{tag_to_atom(lists:reverse(Gat)),lists:reverse(Di),Rest,N};
1138
1259
tag(Fd,<<$\r:8,Rest/binary>>,N,Gat,Di,Now) ->
1139
1260
tag(Fd,Rest,N+1,Gat,Di,Now);
1140
1261
tag(Fd,<<$::8,IdAndRest/binary>>,N,Gat,Di,tag) ->
1317
{ets:select_count(cdv_dump_index_table,count_ms("=proc")),
1318
ets:select_count(cdv_dump_index_table,count_ms("=ets")),
1319
ets:select_count(cdv_dump_index_table,count_ms("=fun"))}.
1322
[{{Tag,'_','_'},[],[true]}].
1325
procs_summary(File) ->
1326
AllProcs = ets:lookup(cdv_dump_index_table,"=proc"),
1328
R = lists:map(fun({"=proc",Pid,Start}) ->
1330
get_procinfo(Fd,fun main_procinfo/4,
1331
?initial_proc_record(Pid))
1435
{count_index(?proc),count_index(?ets),count_index(?fu),count_index(?timer)}.
1438
%%-----------------------------------------------------------------
1439
%% Page with all processes
1441
%% If there are less than ?max_sort_process_num processes in the dump,
1442
%% we will store the list of processes in the server state in order to
1443
%% allow sorting according to the different columns of the
1444
%% table. Since ?max_sort_process_num=:=?items_chunk_size, there will
1445
%% never be more than one chunk in this case.
1447
%% If there are more than ?max_sort_process_num processes in the dump,
1448
%% no sorting will be allowed, and the processes must be read (chunk
1449
%% by chunk) from the file each time the page is opened. This is to
1450
%% avoid really big data in the server state.
1451
procs_summary(SessionId,TW,_,State=#state{procs_summary=too_many}) ->
1452
chunk_page(SessionId,State#state.file,TW,?proc,processes,
1453
{no_sort,State#state.shared_heap},procs_summary_parsefun()),
1455
procs_summary(SessionId,TW,SortOn,State) ->
1457
case State#state.procs_summary of
1458
undefined -> % first time - read from file
1459
Fd = open(State#state.file),
1460
{PS,_}=lookup_and_parse_index_chunk(first_chunk_pointer(?proc),
1461
Fd,procs_summary_parsefun()),
1467
{SortedPS,NewSorted} = do_sort_procs(SortOn,ProcsSummary,State#state.sorted),
1469
crashdump_viewer_html:chunk_page(processes,SessionId,TW,
1470
{SortOn,State#state.shared_heap},
1472
crashdump_viewer_html:chunk(SessionId,done,HtmlInfo),
1473
State#state{procs_summary=ProcsSummary,sorted=NewSorted}.
1475
procs_summary_parsefun() ->
1477
get_procinfo(Fd,fun main_procinfo/4,#proc{pid=Pid})
1480
%%-----------------------------------------------------------------
1481
%% Page with one process
1337
1482
get_proc_details(File,Pid) ->
1338
DumpVsn = ets:lookup_element(cdv_dump_index_table,"=erl_crash_dump",2),
1339
case ets:match(cdv_dump_index_table,{"=proc",Pid,'$1'}) of
1483
[{DumpVsn,_}] = lookup_index(?erl_crash_dump),
1484
case lookup_index(?proc,Pid) of
1341
1486
Fd = open(File),
1342
1487
pos_bof(Fd,Start),
1344
1489
case DumpVsn of
1346
1491
%% Old version (translated)
1347
?initial_proc_record(Pid);
1349
(?initial_proc_record(Pid))#proc{
1350
stack_dump=if_exist("=proc_stack",Pid),
1351
msg_q=if_exist("=proc_messages",Pid),
1352
dict=if_exist("=proc_dictionary",Pid),
1353
debug_dict=if_exist("=debug_proc_dictionary",Pid)}
1495
stack_dump=if_exist(?proc_stack,Pid),
1496
msg_q=if_exist(?proc_messages,Pid),
1497
dict=if_exist(?proc_dictionary,Pid),
1498
debug_dict=if_exist(?debug_proc_dictionary,Pid)}
1355
1500
Proc = get_procinfo(Fd,fun all_procinfo/4,Proc0),
2568
2714
cdvbin(Sz,Pos) ->
2569
2715
"#CDVBin<"++integer_to_list(Sz)++","++integer_to_list(Pos)++">".
2718
%%-----------------------------------------------------------------
2719
%% Functions for accessing the cdv_dump_index_table
2720
reset_index_table() ->
2721
ets:delete_all_objects(cdv_dump_index_table).
2723
insert_index(Tag,Id,Pos) ->
2724
ets:insert(cdv_dump_index_table,{{Tag,Pos},Id}).
2726
lookup_index(Tag) ->
2727
lookup_index(Tag,'$2').
2728
lookup_index(Tag,Id) ->
2729
ets:select(cdv_dump_index_table,[{{{Tag,'$1'},Id},[],[{{Id,'$1'}}]}]).
2731
lookup_index_chunk({'#CDVFirstChunk',Tag,Id}) ->
2732
ets:select(cdv_dump_index_table,
2733
[{{{Tag,'$1'},Id},[],[{{Id,'$1'}}]}],
2735
lookup_index_chunk(Cont) ->
2738
%% Create a tag which can be used instead of an ets Continuation for
2739
%% the first call to lookup_index_chunk.
2740
first_chunk_pointer({Tag,Id}) ->
2741
{'#CDVFirstChunk',Tag,Id};
2742
first_chunk_pointer(Tag) ->
2743
first_chunk_pointer({Tag,'$2'}).
2746
ets:select_count(cdv_dump_index_table,[{{{Tag,'_'},'_'},[],[true]}]).
2747
count_index(Tag,Id) ->
2748
ets:select_count(cdv_dump_index_table,[{{{Tag,'_'},Id},[],[true]}]).
2751
%%-----------------------------------------------------------------
2752
%% Convert tags read from crashdump to atoms used as first part of key
2753
%% in cdv_dump_index_table
2754
tag_to_atom("allocated_areas") -> ?allocated_areas;
2755
tag_to_atom("allocator") -> ?allocator;
2756
tag_to_atom("atoms") -> ?atoms;
2757
tag_to_atom("binary") -> ?binary;
2758
tag_to_atom("debug_proc_dictionary") -> ?debug_proc_dictionary;
2759
tag_to_atom("end") -> ?ende;
2760
tag_to_atom("erl_crash_dump") -> ?erl_crash_dump;
2761
tag_to_atom("ets") -> ?ets;
2762
tag_to_atom("fun") -> ?fu;
2763
tag_to_atom("hash_table") -> ?hash_table;
2764
tag_to_atom("hidden_node") -> ?hidden_node;
2765
tag_to_atom("index_table") -> ?index_table;
2766
tag_to_atom("instr_data") -> ?instr_data;
2767
tag_to_atom("internal_ets") -> ?internal_ets;
2768
tag_to_atom("loaded_modules") -> ?loaded_modules;
2769
tag_to_atom("memory") -> ?memory;
2770
tag_to_atom("mod") -> ?mod;
2771
tag_to_atom("no_distribution") -> ?no_distribution;
2772
tag_to_atom("node") -> ?node;
2773
tag_to_atom("not_connected") -> ?not_connected;
2774
tag_to_atom("num_atoms") -> ?num_atoms;
2775
tag_to_atom("old_instr_data") -> ?old_instr_data;
2776
tag_to_atom("port") -> ?port;
2777
tag_to_atom("proc") -> ?proc;
2778
tag_to_atom("proc_dictionary") -> ?proc_dictionary;
2779
tag_to_atom("proc_heap") -> ?proc_heap;
2780
tag_to_atom("proc_messages") -> ?proc_messages;
2781
tag_to_atom("proc_stack") -> ?proc_stack;
2782
tag_to_atom("timer") -> ?timer;
2783
tag_to_atom("visible_node") -> ?visible_node;
2784
tag_to_atom(UnknownTag) ->
2785
io:format("WARNING: Found unexpected tag:~s~n",[UnknownTag]),
2786
list_to_atom(UnknownTag).
2788
%%%-----------------------------------------------------------------
2789
%%% Create a page by sending chunk by chunk to crashdump_viewer_html
2790
chunk_page(SessionId,File,TW,What,HtmlCB,HtmlExtra,ParseFun) ->
2792
case lookup_and_parse_index_chunk(first_chunk_pointer(What),Fd,ParseFun) of
2794
crashdump_viewer_html:chunk_page(HtmlCB,SessionId,TW,HtmlExtra,done);
2796
HtmlInfo = crashdump_viewer_html:chunk_page(
2798
SessionId,TW,HtmlExtra,Chunk),
2799
chunk_page_1(Fd,HtmlInfo,SessionId,ParseFun,
2800
lookup_and_parse_index_chunk(Cont,Fd,ParseFun))
2803
chunk_page_1(_Fd,HtmlInfo,SessionId,_ParseFun,done) ->
2804
crashdump_viewer_html:chunk(SessionId,done,HtmlInfo);
2805
chunk_page_1(Fd,HtmlInfo,SessionId,ParseFun,{Chunk,Cont}) ->
2806
crashdump_viewer_html:chunk(SessionId,Chunk,HtmlInfo),
2807
chunk_page_1(Fd,HtmlInfo,SessionId,ParseFun,
2808
lookup_and_parse_index_chunk(Cont,Fd,ParseFun)).
2810
lookup_and_parse_index_chunk(Pointer,Fd,ParseFun) ->
2811
case lookup_index_chunk(Pointer) of
2816
R = lists:map(fun({Id,Start}) ->