diff options
Diffstat (limited to 'lib/observer/test/crashdump_viewer_SUITE.erl')
-rw-r--r-- | lib/observer/test/crashdump_viewer_SUITE.erl | 740 |
1 files changed, 250 insertions, 490 deletions
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl index 28c7853eaf..ab2319da6f 100644 --- a/lib/observer/test/crashdump_viewer_SUITE.erl +++ b/lib/observer/test/crashdump_viewer_SUITE.erl @@ -19,9 +19,11 @@ -module(crashdump_viewer_SUITE). +-include_lib("observer/src/crashdump_viewer.hrl"). + %% Test functions -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, - translate/1,start/1,fini/1,load_file/1, + start_stop/1,load_file/1,not_found_items/1, non_existing/1,not_a_crashdump/1,old_crashdump/1]). -export([init_per_suite/1, end_per_suite/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -30,22 +32,39 @@ -include("test_server_line.hrl"). -include_lib("kernel/include/file.hrl"). --include_lib("stdlib/include/ms_transform.hrl"). - --define(default_timeout, ?t:minutes(30)). --define(sl_alloc_vsns,[r9b]). -define(failed_file,"failed-cases.txt"). +-define(helper_mod,crashdump_helper). + + +init_per_testcase(start_stop, Config) -> + catch crashdump_viewer:stop(), + try + case os:type() of + {unix,darwin} -> + exit("Can not test on MacOSX"); + {unix, _} -> + io:format("DISPLAY ~s~n", [os:getenv("DISPLAY")]), + case ct:get_config(xserver, none) of + none -> ignore; + Server -> os:putenv("DISPLAY", Server) + end; + _ -> ignore + end, + wx:new(), + wx:destroy(), + Config + catch + _:undef -> + {skipped, "No wx compiled for this platform"}; + _:Reason -> + SkipReason = io_lib:format("Start wx failed: ~p", [Reason]), + {skipped, lists:flatten(SkipReason)} + end; init_per_testcase(_Case, Config) -> - DataDir = ?config(data_dir,Config), - Fs = filelib:wildcard(filename:join(DataDir,"*translated*")), - lists:foreach(fun(F) -> file:delete(F) end,Fs), catch crashdump_viewer:stop(), - Dog = ?t:timetrap(?default_timeout), - [{watchdog, Dog}|Config]. + Config. end_per_testcase(Case, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog), case ?config(tc_status,Config) of ok -> ok; @@ -60,8 +79,13 @@ end_per_testcase(Case, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [translate, load_file, non_existing, not_a_crashdump, - old_crashdump]. + [start_stop, + non_existing, + not_a_crashdump, + old_crashdump, + load_file, + not_found_items + ]. groups() -> []. @@ -73,18 +97,13 @@ end_per_group(_GroupName, Config) -> Config. -init_per_suite(doc) -> - ["Create a lot of crashdumps which can be used in the testcases below"]; +%% Create a lot of crashdumps which can be used in the testcases below init_per_suite(Config) when is_list(Config) -> - Dog = ?t:timetrap(?default_timeout), delete_saved(Config), - application:start(inets), % will be using the http client later - httpc:set_options([{ipfamily,inet6fb4}]), DataDir = ?config(data_dir,Config), Rels = [R || R <- [r15b,r16b], ?t:is_release_available(R)] ++ [current], io:format("Creating crash dumps for the following releases: ~p", [Rels]), AllDumps = create_dumps(DataDir,Rels), - ?t:timetrap_cancel(Dog), [{dumps,AllDumps}|Config]. delete_saved(Config) -> @@ -97,54 +116,64 @@ delete_saved(Config) -> ok. -translate(suite) -> - []; -translate(doc) -> - ["Test that crash dumps from OTP R9B can be translated"]; -translate(Config) when is_list(Config) -> - DataDir = ?config(data_dir,Config), - OutFile = filename:join(DataDir,"translated"), - - R9BFiles = filelib:wildcard(filename:join(DataDir,"r9b_dump.*")), - AllFiles = R9BFiles, - lists:foreach( - fun(File) -> - io:format("Translating file: ~s~n",[File]), - ok = crashdump_translate:old2new(File,OutFile), - check_result(File,OutFile) - end, - AllFiles), - ok. +start_stop(Config) when is_list(Config) -> + Dump = hd(?config(dumps,Config)), + timer:sleep(1000), -start(suite) -> - []; -start(doc) -> - ["Test start and stop of the Crashdump Viewer"]; -start(Config) when is_list(Config) -> - %% Set a much shorter timeout here... We don't have all the time in world. - AngryDog = ?t:timetrap(?t:seconds(30)), - Port = start_cdv(), + ProcsBefore = length(processes()), + ok = crashdump_viewer:start(Dump), true = is_pid(whereis(crashdump_viewer_server)), - true = is_pid(whereis(web_tool)), - Html = contents(Port,"start_page"), - "Welcome to the Web BasedErlang Crash Dump Analyser" = strip(Html), + true = is_pid(whereis(cdv_wx)), + true = is_pid(whereis(cdv_proc_wx)), + true = is_pid(whereis(cdv_port_wx)), + true = is_pid(whereis(cdv_ets_wx)), + true = is_pid(whereis(cdv_timer_wx)), + true = is_pid(whereis(cdv_fun_wx)), + true = is_pid(whereis(cdv_atom_wx)), + true = is_pid(whereis(cdv_dist_wx)), + true = is_pid(whereis(cdv_mod_wx)), + timer:sleep(1000), % give some time to live ok = crashdump_viewer:stop(), - timer:sleep(10), % give some time to stop + timer:sleep(100), % give some time to stop undefined = whereis(crashdump_viewer_server), - undefined = whereis(web_tool), - Url = cdv_url(Port,"start_page"), - {error,_} = httpc:request(Url), -% exit(whereis(httpc_manager),kill), - ?t:timetrap_cancel(AngryDog), + undefined = whereis(cdv_wx), + ProcsAfter=length(processes()), + ProcsAfter=ProcsBefore, ok. -fini(Config) when is_list(Config) -> - ok. +%% Try to load nonexisting file +non_existing(Config) when is_list(Config) -> + ExpectedReason = "non-existing-file is not an Erlang crash dump\n", + {error, ExpectedReason} = start_backend("non-existing-file"), + ok = crashdump_viewer:stop(). + +%% Try to load a crashdump of old (earlier than OTP R10B) format +old_crashdump(Config) when is_list(Config) -> + DataDir = ?config(data_dir,Config), + OldFile = filename:join(DataDir,"old_format.dump"), + ExpectedReason = "The crashdump " ++ OldFile ++ + " is in the pre-R10B format, which is no longer supported.\n", + {error, ExpectedReason} = start_backend(OldFile), + ok = crashdump_viewer:stop(). + +%% Try to load a file which is not an erlang crashdump +not_a_crashdump(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir,Config), + F1 = filename:join(PrivDir,"f1"), + F2 = filename:join(PrivDir,"f2"), + + file:write_file(F1,"=unexpected_tag:xyz"), + file:write_file(F2,""), + + ExpReason1 = F1 ++ " is not an Erlang crash dump\n", + ExpReason2 = F2 ++ " is not an Erlang crash dump\n", -load_file(suite) -> - []; -load_file(doc) -> - ["Load files into the tool and view all pages"]; + {error,ExpReason1} = start_backend(F1), + {error,ExpReason2} = start_backend(F2), + + ok = crashdump_viewer:stop(). + +%% Load files into the tool and view all pages load_file(Config) when is_list(Config) -> case ?t:is_debug() of true -> @@ -156,65 +185,33 @@ load_file(Config) when is_list(Config) -> load_file_1(Config) -> DataDir = ?config(data_dir,Config), - Port = start_cdv(), + crashdump_viewer:start_link(), + %% Read both created and predefined dumps AllFiles = filelib:wildcard(filename:join(DataDir,"r*_dump.*")), lists:foreach( fun(File) -> - browse_file(Port,File), - special(Port,File) + Content = browse_file(File), + special(File,Content) end, AllFiles), ok = crashdump_viewer:stop(). -non_existing(suite) -> - []; -non_existing(doc) -> - ["Try to load nonexisting file"]; -non_existing(Config) when is_list(Config) -> - Port = start_cdv(), - Url = "http://localhost:"++Port++"/cdv_erl/crashdump_viewer/read_file", - Html = request_sync(post,{Url,[],[],"path=nonexistingfile"}), - "Please wait..." = title(Html), - "An error occured:nonexistingfile is not an Erlang crash dump" = - strip(wait(10,Port,"redirect")), - ok = crashdump_viewer:stop(). +%% Try to lookup nonexisting process, port and node +not_found_items(Config) -> + Dump = hd(?config(dumps,Config)), -old_crashdump(doc) -> - ["Try to load nonexisting file"]; -old_crashdump(Config) when is_list(Config) -> - Port = start_cdv(), - DataDir = ?config(data_dir, Config), - OldCrashDump = filename:join(DataDir, "old_format.dump"), - Url = "http://localhost:"++Port++"/cdv_erl/crashdump_viewer/read_file", - Html = request_sync(post,{Url,[],[],"path="++OldCrashDump}), - "Please wait..." = title(Html), - Str = "An error occured:The crashdump "++OldCrashDump++ - " is in the pre-R10B format, which is no longer supported.", - Str = strip(wait(10,Port,"redirect")), - ok = crashdump_viewer:stop(). + ok = start_backend(Dump), + {ok,#general_info{},_} = crashdump_viewer:general_info(), -not_a_crashdump(suite) -> - []; -not_a_crashdump(doc) -> - ["Try to load a file which is not an erlang crashdump"]; -not_a_crashdump(Config) when is_list(Config) -> - Port = start_cdv(), - NoCrashdump = code:which(?MODULE), - Url = "http://localhost:"++Port++"/cdv_erl/crashdump_viewer/read_file", - Html = request_sync(post,{Url,[],[],"path="++NoCrashdump}), - "Please wait..." = title(Html), - Str = "An error occured:"++NoCrashdump++" is not an Erlang crash dump", - Str = strip(wait(10,Port,"redirect")), - ok = crashdump_viewer:stop(), -% exit(whereis(httpc_manager),kill), - ok. - + {error,not_found} = crashdump_viewer:proc_details("<1111.1111.1111>"), + {error,not_found} = crashdump_viewer:port("#Port<1111.1111>"), + {error,not_found} = crashdump_viewer:node_info("1111"), + ok = crashdump_viewer:stop(). -end_per_suite(doc) -> - ["Remove generated crashdumps"]; +%% Remove generated crashdumps end_per_suite(Config) when is_list(Config) -> Dumps = ?config(dumps,Config), DataDir = ?config(data_dir,Config), @@ -240,387 +237,165 @@ end_per_suite(Config) when is_list(Config) -> %%%----------------------------------------------------------------- %%% Internal -start_cdv() -> - ?t:capture_start(), - ok = crashdump_viewer:start(), - "WebTool is available at http://localhost:" ++ Where = - lists:flatten(?t:capture_get()), - ?t:capture_stop(), - [Port|_] = string:tokens(Where,"/"), - Port. - - -check_result(File,OutFile) -> - {ok,#file_info{size=FS}} = file:read_file_info(File), - {ok,#file_info{size=OFS}} = file:read_file_info(OutFile), - Rel = - if OFS > 0 -> FS/OFS; - true -> 1.25 - end, - if Rel>0.75, Rel<1.25 -> ok; - true -> ?t:fail({unreasonable_size,File,FS,OFS}) - end, - {ok,Fd} = file:open(OutFile,[read]), - "=erl_crash_dump:0.0\n" = io:get_line(Fd,''), - case is_truncated(File) of - true -> - ok; - false -> - {ok,_} = file:position(Fd,{eof,-5}), - case io:get_line(Fd,'') of - "=end\n" -> ok; - Other -> ?t:fail({truncated,File,Other}) - end - end, - ok = file:close(Fd). - - -%% Read a page and check that the page title matches Title -contents(Port,Link) -> - Url = cdv_url(Port,Link), - request_sync(get,{Url,[]}). - -cdv_url(Port,Link) -> - "http://localhost:" ++ Port ++ "/cdv_erl/crashdump_viewer/" ++ Link. - -request_sync(Method,HTTPReqCont) -> - case httpc:request(Method, - HTTPReqCont, - [{timeout,30000}], - [{full_result, false}]) of - {ok,{200,Html}} -> - Html; - {ok,{Code,Html}} -> - io:format("~s\n", [Html]), - io:format("Received ~w from httpc:request(...) with\nMethod=~w\n" - "HTTPReqCont=~p\n", - [Code,Method,HTTPReqCont]), - ?t:fail(); - Other -> - io:format( - "Received ~w from httpc:request(...) with\nMethod=~w\n" - "HTTPReqCont=~p\n", - [Other,Method,HTTPReqCont]), - ?t:fail() - end. - - +%%%----------------------------------------------------------------- +%%% Start the crashdump_viewer backend and load a dump +start_backend(File) -> + crashdump_viewer:start_link(), + register_progress_handler(), + ok = crashdump_viewer:read_file(File), + wait_for_progress_done(). +%%%----------------------------------------------------------------- +%%% Simulate the progress handler in observer_lib +register_progress_handler() -> + register(cdv_progress_handler,self()). -strip([$<|Html]) -> - strip(drop_tag(Html)); -strip([$\n|Html]) -> - strip(Html); -strip([X|Html]) -> - [X|strip(Html)]; -strip([]) -> - []. -drop_tag([$>|Html]) -> - Html; -drop_tag([_|Html]) -> - drop_tag(Html). - -title(Port,Link,Title) -> - Html = contents(Port,Link), - Title = title(Html). - -wait(0,_Port,Link) -> - ?t:fail({wait,Link,timeout}); -wait(Time,Port,Link) -> - Html = contents(Port,Link), - case title(Html) of - "Please wait..." -> - timer:sleep(1000), - wait(Time-1,Port,Link); - _Title -> - Html +wait_for_progress_done() -> + receive + {progress,{error,Reason}} -> + unregister(cdv_progress_handler), + {error,lists:flatten(Reason)}; + {progress,{ok,done}} -> + unregister(cdv_progress_handler), + ok; + {progress,_} -> + wait_for_progress_done() end. -title([$<,$T,$I,$T,$L,$E,$>|Html]) -> - title_end(Html); -title([_|Html]) -> - title(Html); -title([]) -> - []. - -title_end([$<,$/,$T,$I,$T,$L,$E,$>|_]) -> - []; -title_end([X|Html]) -> - [X|title_end(Html)]. - - %%%----------------------------------------------------------------- %%% General check of what is displayed for a dump -browse_file(Port,File) -> +browse_file(File) -> io:format("Browsing file: ~s~n",[File]), - %% The page where a filename can be entered - title(Port,"read_file_frame","Read File"), - - %% Load a file - Url = "http://localhost:"++Port++"/cdv_erl/crashdump_viewer/read_file", - Html = request_sync(post,{Url,[],[],"path="++File}), - "Please wait..." = title(Html), - "Crashdump Viewer Start Page" = title(wait(10,Port,"start_page")), - - %% The frame with the initial information for a dump - title(Port,"initial_info_frame","General Information"), - - %% Topmost frame of the page - FilenameFrame = contents(Port,"filename_frame"), - Match = "FilenameCrashdump currently viewed:" ++ File, - true = lists:prefix(Match,strip(FilenameFrame)), - - %% Toggle a menu item and check that it explodes/collapses - title(Port,"menu_frame","Menu"), - exploded = toggle_menu(Port), - collapsed = toggle_menu(Port), - - %% Open each page in menu and check that correct title is shown - title(Port,"general_info","General Information"), - title(Port,"processes","Process Information"), - title(Port,"sort_procs?sort=state","Process Information"), - title(Port,"sort_procs?sort=state","Process Information"), - title(Port,"sort_procs?sort=pid","Process Information"), - title(Port,"sort_procs?sort=pid","Process Information"), - title(Port,"sort_procs?sort=msg_q_len","Process Information"), - title(Port,"sort_procs?sort=msg_q_len","Process Information"), - title(Port,"sort_procs?sort=reds","Process Information"), - title(Port,"sort_procs?sort=reds","Process Information"), - title(Port,"sort_procs?sort=mem","Process Information"), - title(Port,"sort_procs?sort=mem","Process Information"), - title(Port,"sort_procs?sort=name","Process Information"), - title(Port,"sort_procs?sort=name","Process Information"), - title(Port,"sort_procs?sort=init_func","Process Information"), - title(Port,"sort_procs?sort=init_func","Process Information"), - title(Port,"ports","Port Information"), - title(Port,"ets_tables","ETS Table Information"), - title(Port,"timers","Timer Information"), - title(Port,"fun_table","Fun Information"), - title(Port,"atoms","Atoms"), - title(Port,"dist_info","Distribution Information"), - title(Port,"loaded_modules","Loaded Modules Information"), - title(Port,"hash_tables","Hash Table Information"), - title(Port,"index_tables","Index Table Information"), - title(Port,"memory","Memory Information"), - title(Port,"allocated_areas","Information about allocated areas"), - title(Port,"allocator_info","Allocator Information"), - - case is_truncated(File) of - true -> - ok; - _ -> - proc_details(Port), - port_details(Port), - title(Port,"loaded_mod_details?mod=kernel","kernel") - end, - - ok. - - -special(Port,File) -> + ok = start_backend(File), + + {ok,_GI=#general_info{},_GenTW} = crashdump_viewer:general_info(), + {ok,Procs,_ProcsTW} = crashdump_viewer:processes(), + {ok,Ports,_PortsTW} = crashdump_viewer:ports(), + {ok,_Ets,_EtsTW} = crashdump_viewer:ets_tables(all), + {ok,_IntEts,_IntEtsTW} = crashdump_viewer:internal_ets_tables(), + {ok,_Timers,_TimersTW} = crashdump_viewer:timers(all), + {ok,_Funs,_FunsTW} = crashdump_viewer:funs(), + {ok,_Atoms,_AtomsTW} = crashdump_viewer:atoms(), + {ok,Nodes,_NodesTW} = crashdump_viewer:dist_info(), + {ok,Mods,_ModsTW} = crashdump_viewer:loaded_modules(), + {ok,_Mem,_MemTW} = crashdump_viewer:memory(), + {ok,_AllocAreas,_AreaTW} = crashdump_viewer:allocated_areas(), + {ok,_AllocINfo,_AllocInfoTW} = crashdump_viewer:allocator_info(), + {ok,_HashTabs,_HashTabsTW} = crashdump_viewer:hash_tables(), + {ok,_IndexTabs,_IndexTabsTW} = crashdump_viewer:index_tables(), + + lookat_all_pids(Procs), + lookat_all_ports(Ports), + lookat_all_mods(Mods), + lookat_all_nodes(Nodes), + + Procs. % used as second arg to special/2 + +special(File,Procs) -> case filename:extension(File) of ".full_dist" -> - contents(Port,"processes"), - AllProcs = contents(Port,"sort_procs?sort=name"), - %% I registered a process as aaaaaaaa in the full_dist dumps %% to make sure it will be the first in the list when sorted %% on names. There are some special data here, so I'll thoroughly %% read the process details for this process. Other processes %% are just briefly traversed. - {Pid,Rest1} = get_first_process(AllProcs), - - ProcDetails = contents(Port,"proc_details?pid=" ++ Pid), - ProcTitle = "Process " ++ Pid, - ProcTitle = title(ProcDetails), - title(Port,"ets_tables?pid="++Pid,"ETS Tables for Process "++Pid), - title(Port,"timers?pid="++Pid,"Timers for Process "++Pid), - - case filename:basename(File) of - "r10b_dump.full_dist" -> - [MsgQueueLink,DictLink,StackDumpLink] = - expand_memory_links(ProcDetails), - MsgQueue = contents(Port,MsgQueueLink), - "MsgQueue" = title(MsgQueue), - title(Port,DictLink,"Dictionary"), - title(Port,StackDumpLink,"StackDump"), - - ExpandBinaryLink = expand_binary_link(MsgQueue), - title(Port,ExpandBinaryLink,"Expanded binary"), - lookat_all_pids(Port,Rest1); - _ -> - ok - end; - ".strangemodname" -> - AllMods = contents(Port,"loaded_modules"), - open_all_modules(Port,AllMods), + [#proc{pid=Pid0}|_Rest] = lists:keysort(#proc.name,Procs), + Pid = pid_to_list(Pid0), + {ok,ProcDetails=#proc{},[]} = crashdump_viewer:proc_details(Pid), + + #proc{dict=Dict} = ProcDetails, + + ['#CDVBin',Offset,Size,Pos] = proplists:get_value(bin,Dict), + {ok,<<_:Size/binary>>} = + crashdump_viewer:expand_binary({Offset,Size,Pos}), + {ok,'#CDVTruncatedBinary'} = + crashdump_viewer:expand_binary({Offset,Size+1,Pos}), + ['#CDVBin',SOffset,SSize,SPos] = proplists:get_value(sub_bin,Dict), + {ok,<<_:SSize/binary>>} = + crashdump_viewer:expand_binary({SOffset,SSize,SPos}), + + ['#CDVPid',X1,Y1,Z1] = proplists:get_value(ext_pid,Dict), + ChannelStr1 = integer_to_list(X1), + ExtPid = + "<" ++ ChannelStr1 ++ "." ++ + integer_to_list(Y1) ++ "." ++ + integer_to_list(Z1) ++ ">", + {error,{other_node,ChannelStr1}} = + crashdump_viewer:proc_details(ExtPid), + + ['#CDVPort',X2,Y2] = proplists:get_value(port,Dict), + ChannelStr2 = integer_to_list(X2), + Port = "#Port<"++ChannelStr2++"."++integer_to_list(Y2)++">", + {ok,_PortDetails=#port{},[]} = crashdump_viewer:port(Port), + + ['#CDVPort',X3,Y3] = proplists:get_value(ext_port,Dict), + ChannelStr3 = integer_to_list(X3), + ExtPort = "#Port<"++ChannelStr3++"."++integer_to_list(Y3)++">", + {error,{other_node,ChannelStr3}} = crashdump_viewer:port(ExtPort), + + {ok,[_Ets=#ets_table{}],[]} = crashdump_viewer:ets_tables(Pid), + {ok,[_Timer=#timer{}],[]} = crashdump_viewer:timers(Pid), + + {ok,Mod1=#loaded_mod{},[]} = + crashdump_viewer:loaded_mod_details(atom_to_list(?helper_mod)), + #loaded_mod{current_size=CS, old_size=OS, + old_attrib=A,old_comp_info=C}=Mod1, + true = is_integer(CS), + true = (CS==OS), + true = (A=/=undefined), + true = (C=/=undefined), + {ok,Mod2=#loaded_mod{},[]} = + crashdump_viewer:loaded_mod_details("application"), + #loaded_mod{old_size="No old code exists", + old_attrib=undefined, + old_comp_info=undefined}=Mod2, ok; - %%! No longer needed - all atoms are shown on one page!! - %% ".250atoms" -> - %% Html1 = contents(Port,"atoms"), - %% NextLink1 = next_link(Html1), - %% "Atoms" = title(Html1), - %% Html2 = contents(Port,NextLink1), - %% NextLink2 = next_link(Html2), - %% "Atoms" = title(Html2), - %% Html3 = contents(Port,NextLink2), - %% "" = next_link(Html3), - %% "Atoms" = title(Html3); - _ -> - ok - end, - case filename:basename(File) of - "r10b_dump." ++ _ -> - lookat_all_pids(Port,contents(Port,"processes")); - "r11b_dump." ++ _ -> - lookat_all_pids(Port,contents(Port,"processes")); + %% ".strangemodname" -> + %% {ok,Mods,[]} = crashdump_viewer:loaded_modules(), + %% lookat_all_mods(Mods), + %% ok; + %% ".sort" -> + %% %% sort ports, atoms and modules ???? + %% ok; + %% ".trunc" -> + %% %% ???? + %% ok; _ -> ok end, ok. -lookat_all_pids(Port,Pids) -> - case get_first_process(Pids) of - {Pid,Rest} -> - ProcDetails = contents(Port,"proc_details?pid=" ++ Pid), - ProcTitle = "Process " ++ Pid, - ProcTitle = title(ProcDetails), - title(Port,"ets_tables?pid="++Pid,"ETS Tables for Process "++Pid), - title(Port,"timers?pid="++Pid,"Timers for Process "++Pid), - - MemoryLinks = expand_memory_links(ProcDetails), - lists:foreach( - fun(Link) -> - Cont = contents(Port,Link), - true = lists:member(title(Cont), - ["MsgQueue", - "Dictionary", - "StackDump"]) - end, - MemoryLinks), - lookat_all_pids(Port,Rest); - false -> - ok - end. - - -get_first_process([]) -> - false; -get_first_process(Html) -> - case Html of - "<TD><A HREF=\"./proc_details?pid=" ++ Rest -> - {string:sub_word(Rest,1,$"),Rest}; - [_H|T] -> - get_first_process(T) - end. - -expand_memory_links(Html) -> - case Html of - "<B>MsgQueue</B></TD><TD COLSPAN=3><A HREF=\"./" ++ Rest -> - [string:sub_word(Rest,1,$")|expand_memory_links(Rest)]; - "<B>Dictionary</B></TD><TD COLSPAN=3><A HREF=\"./" ++ Rest -> - [string:sub_word(Rest,1,$")|expand_memory_links(Rest)]; - "<B>StackDump</B></TD><TD COLSPAN=3><A HREF=\"./" ++ Rest -> - [string:sub_word(Rest,1,$")]; - [_H|T] -> - expand_memory_links(T); - [] -> - [] - end. - -expand_binary_link(Html) -> - case Html of - "<A HREF=\"./expand_binary?pos=" ++ Rest -> - "expand_binary?pos=" ++ string:sub_word(Rest,1,$"); - [_H|T] -> - expand_binary_link(T) - end. - -open_all_modules(Port,Modules) -> - case get_first_module(Modules) of - {Module,Rest} -> - ModuleDetails = contents(Port,"loaded_mod_details?mod=" ++ Module), - ModTitle = http_uri:decode(Module), - ModTitle = title(ModuleDetails), - open_all_modules(Port,Rest); - false -> - ok - end. - -get_first_module([]) -> - false; -get_first_module(Html) -> - case Html of - "<TD><A HREF=\"loaded_mod_details?mod=" ++ Rest -> - {string:sub_word(Rest,1,$"),Rest}; - [_H|T] -> - get_first_module(T) - end. - -%% next_link(Html) -> -%% case Html of -%% "<A HREF=\"./next?pos=" ++ Rest -> -%% "next?pos=" ++ string:sub_word(Rest,1,$"); -%% [_H|T] -> -%% next_link(T); -%% [] -> -%% [] -%% end. - - - -toggle_menu(Port) -> - Html = contents(Port,"toggle?index=4"), - check_toggle(Html). - -check_toggle(Html) -> - case Html of - "<A HREF=\"./toggle?index=4\"><IMG SRC=\"/crashdump_viewer/collapsd.gif\"" ++ _ -> - collapsed; - "<A HREF=\"./toggle?index=4\"><IMG SRC=\"/crashdump_viewer/exploded.gif\"" ++ _ -> - exploded; - [_H|T] -> - check_toggle(T) - end. - - -proc_details(Port) -> - ProcDetails = contents(Port,"proc_details?pid=<0.0.0>"), - "Process <0.0.0>" = title(ProcDetails), - - ExpandLink = expand_link(ProcDetails), - title(Port,ExpandLink,"StackDump"), - - Unknown = contents(Port,"proc_details?pid=<0.9999.0>"), - "Could not find process: <0.9999.0>" = title(Unknown). - -expand_link(Html) -> - case Html of - "<B>StackDump</B></TD><TD COLSPAN=3><A HREF=\"./" ++ Rest -> - string:sub_word(Rest,1,$"); - [_H|T] -> - expand_link(T) - end. - - -port_details(Port) -> - Port0 = contents(Port,"port?port=Port<0.0>"), - Port1 = contents(Port,"port?port=Port<0.1>"), - case title(Port0) of - "#Port<0.0>" -> % R16 or later - "Could not find port: #Port<0.1>" = title(Port1); - "Could not find port: #Port<0.0>" -> % R15 or earlier - "#Port<0.1>" = title(Port1) - end. - -is_truncated(File) -> - case filename:extension(filename:rootname(File)) of - ".trunc" -> true; - _ -> false - end. - +lookat_all_pids([]) -> + ok; +lookat_all_pids([#proc{pid=Pid0}|Procs]) -> + Pid = pid_to_list(Pid0), + {ok,_ProcDetails=#proc{},_ProcTW} = crashdump_viewer:proc_details(Pid), + {ok,_Ets,_EtsTW} = crashdump_viewer:ets_tables(Pid), + {ok,_Timers,_TimersTW} = crashdump_viewer:timers(Pid), + lookat_all_pids(Procs). + +lookat_all_ports([]) -> + ok; +lookat_all_ports([#port{id=Port0}|Procs]) -> + Port = cdv_port_wx:format(Port0), + {ok,_PortDetails=#port{},_PortTW} = crashdump_viewer:port(Port), + lookat_all_ports(Procs). + +lookat_all_mods([]) -> + ok; +lookat_all_mods([#loaded_mod{mod=ModId}|Mods]) -> + ModName = cdv_mod_wx:format(ModId), + {ok,_Mod=#loaded_mod{},_ModTW} = crashdump_viewer:loaded_mod_details(ModName), + lookat_all_mods(Mods). + +lookat_all_nodes([]) -> + ok; +lookat_all_nodes([#nod{channel=Channel0}|Nodes]) -> + Channel = integer_to_list(Channel0), + {ok,_Node=#nod{},_NodeTW} = crashdump_viewer:node_info(Channel), + lookat_all_nodes(Nodes). %%%----------------------------------------------------------------- %%% @@ -629,22 +404,13 @@ create_dumps(DataDir,Rels) -> create_dumps(DataDir,[Rel|Rels],Acc) -> Fun = fun() -> do_create_dumps(DataDir,Rel) end, Pa = filename:dirname(code:which(?MODULE)), - {SlAllocDumps,Dumps,DosDump} = + {Dumps,DosDump} = ?t:run_on_shielded_node(Fun, compat_rel(Rel) ++ "-pa \"" ++ Pa ++ "\""), - create_dumps(DataDir,Rels,SlAllocDumps ++ Dumps ++ Acc ++ DosDump); + create_dumps(DataDir,Rels,Dumps ++ Acc ++ DosDump); create_dumps(_DataDir,[],Acc) -> Acc. do_create_dumps(DataDir,Rel) -> - SlAllocDumps = - case lists:member(Rel,?sl_alloc_vsns) of - true -> - [dump_with_args(DataDir,Rel,"no_sl_alloc","+Se false"), - dump_with_args(DataDir,Rel,"sl_alloc_1","+Se true +Sr 1"), - dump_with_args(DataDir,Rel,"sl_alloc_2","+Se true +Sr 2")]; - false -> - [] - end, CD1 = full_dist_dump(DataDir,Rel), CD2 = dump_with_args(DataDir,Rel,"port_is_unix_fd","-oldshell"), DosDump = @@ -656,9 +422,9 @@ do_create_dumps(DataDir,Rel) -> current -> CD3 = dump_with_args(DataDir,Rel,"instr","+Mim true"), CD4 = dump_with_strange_module_name(DataDir,Rel,"strangemodname"), - {SlAllocDumps, [CD1,CD2,CD3,CD4], DosDump}; + {[CD1,CD2,CD3,CD4], DosDump}; _ -> - {SlAllocDumps, [CD1,CD2], DosDump} + {[CD1,CD2], DosDump} end. @@ -674,23 +440,17 @@ full_dist_dump(DataDir,Rel) -> {ok,N4} = ?t:start_node(n4,peer,Opt ++ [{args,"-hidden " ++ Pz}]), Creator = self(), - HelperMod = crashdump_helper, - - P1 = rpc:call(N1,HelperMod,n1_proc,[N2,Creator]), - P2 = rpc:call(N2,HelperMod,remote_proc,[P1,Creator]), - P3 = rpc:call(N3,HelperMod,remote_proc,[P1,Creator]), - P4 = rpc:call(N4,HelperMod,remote_proc,[P1,Creator]), + P1 = rpc:call(N1,?helper_mod,n1_proc,[N2,Creator]), + P2 = rpc:call(N2,?helper_mod,remote_proc,[P1,Creator]), + P3 = rpc:call(N3,?helper_mod,remote_proc,[P1,Creator]), + P4 = rpc:call(N4,?helper_mod,remote_proc,[P1,Creator]), get_response(P2), get_response(P3), get_response(P4), get_response(P1), - L = lists:seq(0,255), - BigMsg = {message,list_to_binary(L),L}, - Port = hd(erlang:ports()), - {aaaaaaaa,N1} ! {short,message,1,2.5,"hello world",Port,{}}, - {aaaaaaaa,N1} ! BigMsg, + {aaaaaaaa,N1} ! {hello,from,other,node}, % distribution message ?t:stop_node(N3), DumpName = "full_dist", |