1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2003-2012. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: 20: -module(crashdump_viewer_SUITE). 21: 22: %% Test functions 23: -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, 24: translate/1,start/1,fini/1,load_file/1, 25: non_existing/1,not_a_crashdump/1,old_crashdump/1]). 26: -export([init_per_suite/1, end_per_suite/1]). 27: -export([init_per_testcase/2, end_per_testcase/2]). 28: 29: -include_lib("common_test/include/ct.hrl"). 30: -include("test_server_line.hrl"). 31: -include_lib("kernel/include/file.hrl"). 32: 33: -include_lib("stdlib/include/ms_transform.hrl"). 34: 35: -define(default_timeout, ?t:minutes(30)). 36: -define(sl_alloc_vsns,[r9b]). 37: -define(failed_file,"failed-cases.txt"). 38: 39: init_per_testcase(_Case, Config) -> 40: DataDir = ?config(data_dir,Config), 41: Fs = filelib:wildcard(filename:join(DataDir,"*translated*")), 42: lists:foreach(fun(F) -> file:delete(F) end,Fs), 43: catch crashdump_viewer:stop(), 44: Dog = ?t:timetrap(?default_timeout), 45: [{watchdog, Dog}|Config]. 46: end_per_testcase(Case, Config) -> 47: Dog=?config(watchdog, Config), 48: ?t:timetrap_cancel(Dog), 49: case ?config(tc_status,Config) of 50: ok -> 51: ok; 52: _Fail -> 53: File = filename:join(?config(data_dir,Config),?failed_file), 54: {ok,Fd}=file:open(File,[append]), 55: file:write(Fd,io_lib:format("~w.~n",[Case])), 56: file:close(Fd) 57: end, 58: ok. 59: 60: suite() -> [{ct_hooks,[ts_install_cth]}]. 61: 62: all() -> 63: [translate, load_file, non_existing, not_a_crashdump, 64: old_crashdump]. 65: 66: groups() -> 67: []. 68: 69: init_per_group(_GroupName, Config) -> 70: Config. 71: 72: end_per_group(_GroupName, Config) -> 73: Config. 74: 75: 76: init_per_suite(doc) -> 77: ["Create a lot of crashdumps which can be used in the testcases below"]; 78: init_per_suite(Config) when is_list(Config) -> 79: Dog = ?t:timetrap(?default_timeout), 80: delete_saved(Config), 81: application:start(inets), % will be using the http client later 82: httpc:set_options([{ipfamily,inet6fb4}]), 83: DataDir = ?config(data_dir,Config), 84: Rels = [R || R <- [r14b,r15b], ?t:is_release_available(R)] ++ [current], 85: io:format("Creating crash dumps for the following releases: ~p", [Rels]), 86: AllDumps = create_dumps(DataDir,Rels), 87: ?t:timetrap_cancel(Dog), 88: [{dumps,AllDumps}|Config]. 89: 90: delete_saved(Config) -> 91: DataDir = ?config(data_dir,Config), 92: file:delete(filename:join(DataDir,?failed_file)), 93: SaveDir = filename:join(DataDir,"save"), 94: Dumps = filelib:wildcard(filename:join(SaveDir,"*")), 95: lists:foreach(fun(F) -> file:delete(F) end, Dumps), 96: file:del_dir(SaveDir), 97: ok. 98: 99: 100: translate(suite) -> 101: []; 102: translate(doc) -> 103: ["Test that crash dumps from OTP R9B can be translated"]; 104: translate(Config) when is_list(Config) -> 105: DataDir = ?config(data_dir,Config), 106: OutFile = filename:join(DataDir,"translated"), 107: 108: R9BFiles = filelib:wildcard(filename:join(DataDir,"r9b_dump.*")), 109: AllFiles = R9BFiles, 110: lists:foreach( 111: fun(File) -> 112: io:format("Translating file: ~s~n",[File]), 113: ok = crashdump_translate:old2new(File,OutFile), 114: check_result(File,OutFile) 115: end, 116: AllFiles), 117: ok. 118: 119: start(suite) -> 120: []; 121: start(doc) -> 122: ["Test start and stop of the Crashdump Viewer"]; 123: start(Config) when is_list(Config) -> 124: %% Set a much shorter timeout here... We don't have all the time in world. 125: AngryDog = ?t:timetrap(?t:seconds(30)), 126: Port = start_cdv(), 127: true = is_pid(whereis(crashdump_viewer_server)), 128: true = is_pid(whereis(web_tool)), 129: Html = contents(Port,"start_page"), 130: "Welcome to the Web BasedErlang Crash Dump Analyser" = strip(Html), 131: ok = crashdump_viewer:stop(), 132: timer:sleep(10), % give some time to stop 133: undefined = whereis(crashdump_viewer_server), 134: undefined = whereis(web_tool), 135: Url = cdv_url(Port,"start_page"), 136: {error,_} = httpc:request(Url), 137: % exit(whereis(httpc_manager),kill), 138: ?t:timetrap_cancel(AngryDog), 139: ok. 140: 141: fini(Config) when is_list(Config) -> 142: ok. 143: 144: load_file(suite) -> 145: []; 146: load_file(doc) -> 147: ["Load files into the tool and view all pages"]; 148: load_file(Config) when is_list(Config) -> 149: case ?t:is_debug() of 150: true -> 151: {skip,"Debug-compiled emulator -- far too slow"}; 152: false -> 153: load_file_1(Config) 154: end. 155: 156: 157: load_file_1(Config) -> 158: DataDir = ?config(data_dir,Config), 159: Port = start_cdv(), 160: 161: AllFiles = filelib:wildcard(filename:join(DataDir,"r*_dump.*")), 162: lists:foreach( 163: fun(File) -> 164: browse_file(Port,File), 165: special(Port,File) 166: end, 167: AllFiles), 168: ok = crashdump_viewer:stop(). 169: 170: non_existing(suite) -> 171: []; 172: non_existing(doc) -> 173: ["Try to load nonexisting file"]; 174: non_existing(Config) when is_list(Config) -> 175: Port = start_cdv(), 176: Url = "http://localhost:"++Port++"/cdv_erl/crashdump_viewer/read_file", 177: Html = request_sync(post,{Url,[],[],"path=nonexistingfile"}), 178: "Please wait..." = title(Html), 179: "An error occured:nonexistingfile is not an Erlang crash dump" = 180: strip(wait(10,Port,"redirect")), 181: ok = crashdump_viewer:stop(). 182: 183: old_crashdump(doc) -> 184: ["Try to load nonexisting file"]; 185: old_crashdump(Config) when is_list(Config) -> 186: Port = start_cdv(), 187: DataDir = ?config(data_dir, Config), 188: OldCrashDump = filename:join(DataDir, "old_format.dump"), 189: Url = "http://localhost:"++Port++"/cdv_erl/crashdump_viewer/read_file", 190: Html = request_sync(post,{Url,[],[],"path="++OldCrashDump}), 191: "Please wait..." = title(Html), 192: Str = "An error occured:The crashdump "++OldCrashDump++ 193: " is in the pre-R10B format, which is no longer supported.", 194: Str = strip(wait(10,Port,"redirect")), 195: ok = crashdump_viewer:stop(). 196: 197: 198: not_a_crashdump(suite) -> 199: []; 200: not_a_crashdump(doc) -> 201: ["Try to load a file which is not an erlang crashdump"]; 202: not_a_crashdump(Config) when is_list(Config) -> 203: Port = start_cdv(), 204: NoCrashdump = code:which(?MODULE), 205: Url = "http://localhost:"++Port++"/cdv_erl/crashdump_viewer/read_file", 206: Html = request_sync(post,{Url,[],[],"path="++NoCrashdump}), 207: "Please wait..." = title(Html), 208: Str = "An error occured:"++NoCrashdump++" is not an Erlang crash dump", 209: Str = strip(wait(10,Port,"redirect")), 210: ok = crashdump_viewer:stop(), 211: % exit(whereis(httpc_manager),kill), 212: ok. 213: 214: 215: 216: end_per_suite(doc) -> 217: ["Remove generated crashdumps"]; 218: end_per_suite(Config) when is_list(Config) -> 219: Dumps = ?config(dumps,Config), 220: DataDir = ?config(data_dir,Config), 221: FailedFile = filename:join(DataDir,?failed_file), 222: case filelib:is_file(FailedFile) of 223: true -> 224: SaveDir = filename:join(DataDir,"save"), 225: file:make_dir(SaveDir), 226: file:copy(FailedFile,filename:join(SaveDir,?failed_file)), 227: lists:foreach( 228: fun(CD) -> 229: File = filename:basename(CD), 230: New = filename:join(SaveDir,File), 231: file:copy(CD,New) 232: end, Dumps); 233: false -> 234: ok 235: end, 236: file:delete(FailedFile), 237: lists:foreach(fun(CD) -> ok = file:delete(CD) end,Dumps), 238: lists:keydelete(dumps,1,Config). 239: 240: 241: %%%----------------------------------------------------------------- 242: %%% Internal 243: start_cdv() -> 244: ?t:capture_start(), 245: ok = crashdump_viewer:start(), 246: "WebTool is available at http://localhost:" ++ Where = 247: lists:flatten(?t:capture_get()), 248: ?t:capture_stop(), 249: [Port|_] = string:tokens(Where,"/"), 250: Port. 251: 252: 253: check_result(File,OutFile) -> 254: {ok,#file_info{size=FS}} = file:read_file_info(File), 255: {ok,#file_info{size=OFS}} = file:read_file_info(OutFile), 256: Rel = 257: if OFS > 0 -> FS/OFS; 258: true -> 1.25 259: end, 260: if Rel>0.75, Rel<1.25 -> ok; 261: true -> ?t:fail({unreasonable_size,File,FS,OFS}) 262: end, 263: {ok,Fd} = file:open(OutFile,[read]), 264: "=erl_crash_dump:0.0\n" = io:get_line(Fd,''), 265: case is_truncated(File) of 266: true -> 267: ok; 268: false -> 269: {ok,_} = file:position(Fd,{eof,-5}), 270: case io:get_line(Fd,'') of 271: "=end\n" -> ok; 272: Other -> ?t:fail({truncated,File,Other}) 273: end 274: end, 275: ok = file:close(Fd). 276: 277: 278: %% Read a page and check that the page title matches Title 279: contents(Port,Link) -> 280: Url = cdv_url(Port,Link), 281: request_sync(get,{Url,[]}). 282: 283: cdv_url(Port,Link) -> 284: "http://localhost:" ++ Port ++ "/cdv_erl/crashdump_viewer/" ++ Link. 285: 286: request_sync(Method,HTTPReqCont) -> 287: case httpc:request(Method, 288: HTTPReqCont, 289: [{timeout,30000}], 290: [{full_result, false}]) of 291: {ok,{200,Html}} -> 292: Html; 293: {ok,{Code,Html}} -> 294: io:format("~s\n", [Html]), 295: io:format("Received ~w from httpc:request(...) with\nMethod=~w\n" 296: "HTTPReqCont=~p\n", 297: [Code,Method,HTTPReqCont]), 298: ?t:fail(); 299: Other -> 300: io:format( 301: "Received ~w from httpc:request(...) with\nMethod=~w\n" 302: "HTTPReqCont=~p\n", 303: [Other,Method,HTTPReqCont]), 304: ?t:fail() 305: end. 306: 307: 308: 309: 310: strip([$<|Html]) -> 311: strip(drop_tag(Html)); 312: strip([$\n|Html]) -> 313: strip(Html); 314: strip([X|Html]) -> 315: [X|strip(Html)]; 316: strip([]) -> 317: []. 318: drop_tag([$>|Html]) -> 319: Html; 320: drop_tag([_|Html]) -> 321: drop_tag(Html). 322: 323: title(Port,Link,Title) -> 324: Html = contents(Port,Link), 325: Title = title(Html). 326: 327: wait(0,_Port,Link) -> 328: ?t:fail({wait,Link,timeout}); 329: wait(Time,Port,Link) -> 330: Html = contents(Port,Link), 331: case title(Html) of 332: "Please wait..." -> 333: timer:sleep(1000), 334: wait(Time-1,Port,Link); 335: _Title -> 336: Html 337: end. 338: 339: title([$<,$T,$I,$T,$L,$E,$>|Html]) -> 340: title_end(Html); 341: title([_|Html]) -> 342: title(Html); 343: title([]) -> 344: []. 345: 346: title_end([$<,$/,$T,$I,$T,$L,$E,$>|_]) -> 347: []; 348: title_end([X|Html]) -> 349: [X|title_end(Html)]. 350: 351: 352: %%%----------------------------------------------------------------- 353: %%% General check of what is displayed for a dump 354: browse_file(Port,File) -> 355: io:format("Browsing file: ~s~n",[File]), 356: 357: %% The page where a filename can be entered 358: title(Port,"read_file_frame","Read File"), 359: 360: %% Load a file 361: Url = "http://localhost:"++Port++"/cdv_erl/crashdump_viewer/read_file", 362: Html = request_sync(post,{Url,[],[],"path="++File}), 363: "Please wait..." = title(Html), 364: "Crashdump Viewer Start Page" = title(wait(10,Port,"start_page")), 365: 366: %% The frame with the initial information for a dump 367: title(Port,"initial_info_frame","General Information"), 368: 369: %% Topmost frame of the page 370: FilenameFrame = contents(Port,"filename_frame"), 371: Match = "FilenameCrashdump currently viewed:" ++ File, 372: true = lists:prefix(Match,strip(FilenameFrame)), 373: 374: %% Toggle a menu item and check that it explodes/collapses 375: title(Port,"menu_frame","Menu"), 376: exploded = toggle_menu(Port), 377: collapsed = toggle_menu(Port), 378: 379: %% Open each page in menu and check that correct title is shown 380: title(Port,"general_info","General Information"), 381: title(Port,"processes","Process Information"), 382: title(Port,"sort_procs?sort=state","Process Information"), 383: title(Port,"sort_procs?sort=state","Process Information"), 384: title(Port,"sort_procs?sort=pid","Process Information"), 385: title(Port,"sort_procs?sort=pid","Process Information"), 386: title(Port,"sort_procs?sort=msg_q_len","Process Information"), 387: title(Port,"sort_procs?sort=msg_q_len","Process Information"), 388: title(Port,"sort_procs?sort=reds","Process Information"), 389: title(Port,"sort_procs?sort=reds","Process Information"), 390: title(Port,"sort_procs?sort=mem","Process Information"), 391: title(Port,"sort_procs?sort=mem","Process Information"), 392: title(Port,"sort_procs?sort=name","Process Information"), 393: title(Port,"sort_procs?sort=name","Process Information"), 394: title(Port,"sort_procs?sort=init_func","Process Information"), 395: title(Port,"sort_procs?sort=init_func","Process Information"), 396: title(Port,"ports","Port Information"), 397: title(Port,"ets_tables","ETS Table Information"), 398: title(Port,"timers","Timer Information"), 399: title(Port,"fun_table","Fun Information"), 400: title(Port,"atoms","Atoms"), 401: title(Port,"dist_info","Distribution Information"), 402: title(Port,"loaded_modules","Loaded Modules Information"), 403: title(Port,"hash_tables","Hash Table Information"), 404: title(Port,"index_tables","Index Table Information"), 405: title(Port,"memory","Memory Information"), 406: title(Port,"allocated_areas","Information about allocated areas"), 407: title(Port,"allocator_info","Allocator Information"), 408: 409: case is_truncated(File) of 410: true -> 411: ok; 412: _ -> 413: proc_details(Port), 414: port_details(Port), 415: title(Port,"loaded_mod_details?mod=kernel","kernel") 416: end, 417: 418: ok. 419: 420: 421: special(Port,File) -> 422: case filename:extension(File) of 423: ".full_dist" -> 424: contents(Port,"processes"), 425: AllProcs = contents(Port,"sort_procs?sort=name"), 426: 427: %% I registered a process as aaaaaaaa in the full_dist dumps 428: %% to make sure it will be the first in the list when sorted 429: %% on names. There are some special data here, so I'll thoroughly 430: %% read the process details for this process. Other processes 431: %% are just briefly traversed. 432: {Pid,Rest1} = get_first_process(AllProcs), 433: 434: ProcDetails = contents(Port,"proc_details?pid=" ++ Pid), 435: ProcTitle = "Process " ++ Pid, 436: ProcTitle = title(ProcDetails), 437: title(Port,"ets_tables?pid="++Pid,"ETS Tables for Process "++Pid), 438: title(Port,"timers?pid="++Pid,"Timers for Process "++Pid), 439: 440: case filename:basename(File) of 441: "r10b_dump.full_dist" -> 442: [MsgQueueLink,DictLink,StackDumpLink] = 443: expand_memory_links(ProcDetails), 444: MsgQueue = contents(Port,MsgQueueLink), 445: "MsgQueue" = title(MsgQueue), 446: title(Port,DictLink,"Dictionary"), 447: title(Port,StackDumpLink,"StackDump"), 448: 449: ExpandBinaryLink = expand_binary_link(MsgQueue), 450: title(Port,ExpandBinaryLink,"Expanded binary"), 451: lookat_all_pids(Port,Rest1); 452: _ -> 453: ok 454: end; 455: ".strangemodname" -> 456: AllMods = contents(Port,"loaded_modules"), 457: open_all_modules(Port,AllMods), 458: ok; 459: %%! No longer needed - all atoms are shown on one page!! 460: %% ".250atoms" -> 461: %% Html1 = contents(Port,"atoms"), 462: %% NextLink1 = next_link(Html1), 463: %% "Atoms" = title(Html1), 464: %% Html2 = contents(Port,NextLink1), 465: %% NextLink2 = next_link(Html2), 466: %% "Atoms" = title(Html2), 467: %% Html3 = contents(Port,NextLink2), 468: %% "" = next_link(Html3), 469: %% "Atoms" = title(Html3); 470: _ -> 471: ok 472: end, 473: case filename:basename(File) of 474: "r10b_dump." ++ _ -> 475: lookat_all_pids(Port,contents(Port,"processes")); 476: "r11b_dump." ++ _ -> 477: lookat_all_pids(Port,contents(Port,"processes")); 478: _ -> 479: ok 480: end, 481: ok. 482: 483: 484: lookat_all_pids(Port,Pids) -> 485: case get_first_process(Pids) of 486: {Pid,Rest} -> 487: ProcDetails = contents(Port,"proc_details?pid=" ++ Pid), 488: ProcTitle = "Process " ++ Pid, 489: ProcTitle = title(ProcDetails), 490: title(Port,"ets_tables?pid="++Pid,"ETS Tables for Process "++Pid), 491: title(Port,"timers?pid="++Pid,"Timers for Process "++Pid), 492: 493: MemoryLinks = expand_memory_links(ProcDetails), 494: lists:foreach( 495: fun(Link) -> 496: Cont = contents(Port,Link), 497: true = lists:member(title(Cont), 498: ["MsgQueue", 499: "Dictionary", 500: "StackDump"]) 501: end, 502: MemoryLinks), 503: lookat_all_pids(Port,Rest); 504: false -> 505: ok 506: end. 507: 508: 509: get_first_process([]) -> 510: false; 511: get_first_process(Html) -> 512: case Html of 513: "<TD><A HREF=\"./proc_details?pid=" ++ Rest -> 514: {string:sub_word(Rest,1,$"),Rest}; 515: [_H|T] -> 516: get_first_process(T) 517: end. 518: 519: expand_memory_links(Html) -> 520: case Html of 521: "<B>MsgQueue</B></TD><TD COLSPAN=3><A HREF=\"./" ++ Rest -> 522: [string:sub_word(Rest,1,$")|expand_memory_links(Rest)]; 523: "<B>Dictionary</B></TD><TD COLSPAN=3><A HREF=\"./" ++ Rest -> 524: [string:sub_word(Rest,1,$")|expand_memory_links(Rest)]; 525: "<B>StackDump</B></TD><TD COLSPAN=3><A HREF=\"./" ++ Rest -> 526: [string:sub_word(Rest,1,$")]; 527: [_H|T] -> 528: expand_memory_links(T); 529: [] -> 530: [] 531: end. 532: 533: expand_binary_link(Html) -> 534: case Html of 535: "<A HREF=\"./expand_binary?pos=" ++ Rest -> 536: "expand_binary?pos=" ++ string:sub_word(Rest,1,$"); 537: [_H|T] -> 538: expand_binary_link(T) 539: end. 540: 541: open_all_modules(Port,Modules) -> 542: case get_first_module(Modules) of 543: {Module,Rest} -> 544: ModuleDetails = contents(Port,"loaded_mod_details?mod=" ++ Module), 545: ModTitle = http_uri:decode(Module), 546: ModTitle = title(ModuleDetails), 547: open_all_modules(Port,Rest); 548: false -> 549: ok 550: end. 551: 552: get_first_module([]) -> 553: false; 554: get_first_module(Html) -> 555: case Html of 556: "<TD><A HREF=\"loaded_mod_details?mod=" ++ Rest -> 557: {string:sub_word(Rest,1,$"),Rest}; 558: [_H|T] -> 559: get_first_module(T) 560: end. 561: 562: %% next_link(Html) -> 563: %% case Html of 564: %% "<A HREF=\"./next?pos=" ++ Rest -> 565: %% "next?pos=" ++ string:sub_word(Rest,1,$"); 566: %% [_H|T] -> 567: %% next_link(T); 568: %% [] -> 569: %% [] 570: %% end. 571: 572: 573: 574: toggle_menu(Port) -> 575: Html = contents(Port,"toggle?index=4"), 576: check_toggle(Html). 577: 578: check_toggle(Html) -> 579: case Html of 580: "<A HREF=\"./toggle?index=4\"><IMG SRC=\"/crashdump_viewer/collapsd.gif\"" ++ _ -> 581: collapsed; 582: "<A HREF=\"./toggle?index=4\"><IMG SRC=\"/crashdump_viewer/exploded.gif\"" ++ _ -> 583: exploded; 584: [_H|T] -> 585: check_toggle(T) 586: end. 587: 588: 589: proc_details(Port) -> 590: ProcDetails = contents(Port,"proc_details?pid=<0.0.0>"), 591: "Process <0.0.0>" = title(ProcDetails), 592: 593: ExpandLink = expand_link(ProcDetails), 594: title(Port,ExpandLink,"StackDump"), 595: 596: Unknown = contents(Port,"proc_details?pid=<0.9999.0>"), 597: "Could not find process: <0.9999.0>" = title(Unknown). 598: 599: expand_link(Html) -> 600: case Html of 601: "<B>StackDump</B></TD><TD COLSPAN=3><A HREF=\"./" ++ Rest -> 602: string:sub_word(Rest,1,$"); 603: [_H|T] -> 604: expand_link(T) 605: end. 606: 607: 608: port_details(Port) -> 609: Port0 = contents(Port,"port?port=Port<0.0>"), 610: Port1 = contents(Port,"port?port=Port<0.1>"), 611: case title(Port0) of 612: "#Port<0.0>" -> % R16 or later 613: "Could not find port: #Port<0.1>" = title(Port1); 614: "Could not find port: #Port<0.0>" -> % R15 or earlier 615: "#Port<0.1>" = title(Port1) 616: end. 617: 618: is_truncated(File) -> 619: case filename:extension(filename:rootname(File)) of 620: ".trunc" -> true; 621: _ -> false 622: end. 623: 624: 625: %%%----------------------------------------------------------------- 626: %%% 627: create_dumps(DataDir,Rels) -> 628: create_dumps(DataDir,Rels,[]). 629: create_dumps(DataDir,[Rel|Rels],Acc) -> 630: Fun = fun() -> do_create_dumps(DataDir,Rel) end, 631: Pa = filename:dirname(code:which(?MODULE)), 632: {SlAllocDumps,Dumps,DosDump} = 633: ?t:run_on_shielded_node(Fun, compat_rel(Rel) ++ "-pa \"" ++ Pa ++ "\""), 634: create_dumps(DataDir,Rels,SlAllocDumps ++ Dumps ++ Acc ++ DosDump); 635: create_dumps(_DataDir,[],Acc) -> 636: Acc. 637: 638: do_create_dumps(DataDir,Rel) -> 639: SlAllocDumps = 640: case lists:member(Rel,?sl_alloc_vsns) of 641: true -> 642: [dump_with_args(DataDir,Rel,"no_sl_alloc","+Se false"), 643: dump_with_args(DataDir,Rel,"sl_alloc_1","+Se true +Sr 1"), 644: dump_with_args(DataDir,Rel,"sl_alloc_2","+Se true +Sr 2")]; 645: false -> 646: [] 647: end, 648: CD1 = full_dist_dump(DataDir,Rel), 649: CD2 = dump_with_args(DataDir,Rel,"port_is_unix_fd","-oldshell"), 650: DosDump = 651: case os:type() of 652: {unix,sunos} -> dos_dump(DataDir,Rel,CD1); 653: _ -> [] 654: end, 655: case Rel of 656: current -> 657: CD3 = dump_with_args(DataDir,Rel,"instr","+Mim true"), 658: CD4 = dump_with_strange_module_name(DataDir,Rel,"strangemodname"), 659: {SlAllocDumps, [CD1,CD2,CD3,CD4], DosDump}; 660: _ -> 661: {SlAllocDumps, [CD1,CD2], DosDump} 662: end. 663: 664: 665: %% Create a dump which has two visible nodes, one hidden and one 666: %% not connected node, and with monitors and links between nodes. 667: full_dist_dump(DataDir,Rel) -> 668: Opt = rel_opt(Rel), 669: Pz = "-pz \"" ++ filename:dirname(code:which(?MODULE)) ++ "\"", 670: PzOpt = [{args,Pz}], 671: {ok,N1} = ?t:start_node(n1,peer,Opt ++ PzOpt), 672: {ok,N2} = ?t:start_node(n2,peer,Opt ++ PzOpt), 673: {ok,N3} = ?t:start_node(n3,peer,Opt ++ PzOpt), 674: {ok,N4} = ?t:start_node(n4,peer,Opt ++ [{args,"-hidden " ++ Pz}]), 675: Creator = self(), 676: 677: HelperMod = crashdump_helper, 678: 679: P1 = rpc:call(N1,HelperMod,n1_proc,[N2,Creator]), 680: P2 = rpc:call(N2,HelperMod,remote_proc,[P1,Creator]), 681: P3 = rpc:call(N3,HelperMod,remote_proc,[P1,Creator]), 682: P4 = rpc:call(N4,HelperMod,remote_proc,[P1,Creator]), 683: 684: get_response(P2), 685: get_response(P3), 686: get_response(P4), 687: get_response(P1), 688: 689: L = lists:seq(0,255), 690: BigMsg = {message,list_to_binary(L),L}, 691: Port = hd(erlang:ports()), 692: {aaaaaaaa,N1} ! {short,message,1,2.5,"hello world",Port,{}}, 693: {aaaaaaaa,N1} ! BigMsg, 694: 695: ?t:stop_node(N3), 696: DumpName = "full_dist", 697: CD = dump(N1,DataDir,Rel,DumpName), 698: 699: ?t:stop_node(N2), 700: ?t:stop_node(N4), 701: CD. 702: 703: get_response(P) -> 704: receive {P,done} -> ok 705: after 3000 -> ?t:fail({get_response_timeout,P,node(P)}) 706: end. 707: 708: 709: dump_with_args(DataDir,Rel,DumpName,Args) -> 710: RelOpt = rel_opt(Rel), 711: Opt = RelOpt ++ [{args,Args}], 712: {ok,N1} = ?t:start_node(n1,peer,Opt), 713: CD = dump(N1,DataDir,Rel,DumpName), 714: ?t:stop_node(n1), 715: CD. 716: 717: %% This dump is added to test OTP-10090 - regarding URL encoding of 718: %% module names in the module detail link. 719: dump_with_strange_module_name(DataDir,Rel,DumpName) -> 720: Opt = rel_opt(Rel), 721: {ok,N1} = ?t:start_node(n1,peer,Opt), 722: 723: Mod = '<mod ule#with?strange%name>', 724: File = atom_to_list(Mod) ++ ".erl", 725: Forms = [{attribute,1,file,{File,1}}, 726: {attribute,1,module,Mod}, 727: {eof,4}], 728: {ok,Mod,Bin} = rpc:call(N1,compile,forms,[Forms,[binary]]), 729: {module,Mod} = rpc:call(N1,code,load_binary,[Mod,File,Bin]), 730: CD = dump(N1,DataDir,Rel,DumpName), 731: ?t:stop_node(n1), 732: CD. 733: 734: dump(Node,DataDir,Rel,DumpName) -> 735: case Rel of 736: _ when Rel<r15b, Rel=/=current -> 737: rpc:call(Node,os,putenv,["ERL_CRASH_DUMP_SECONDS","600"]); 738: _ -> 739: ok 740: end, 741: rpc:call(Node,erlang,halt,[DumpName]), 742: Crashdump0 = filename:join(filename:dirname(code:which(?t)), 743: "erl_crash_dump.n1"), 744: Crashdump1 = filename:join(DataDir, dump_prefix(Rel)++DumpName), 745: ok = rename(Crashdump0,Crashdump1), 746: Crashdump1. 747: 748: rename(From,To) -> 749: ok = check_complete(From), 750: case file:rename(From,To) of 751: {error,exdev} -> 752: {ok,_} = file:copy(From,To), 753: ok = file:delete(From); 754: ok -> 755: ok 756: end. 757: 758: check_complete(File) -> 759: check_complete1(File,10). 760: 761: check_complete1(_File,0) -> 762: {error,enoent}; 763: check_complete1(File,N) -> 764: case file:read_file_info(File) of 765: {error,enoent} -> 766: timer:sleep(500), 767: check_complete1(File,N-1); 768: {ok,#file_info{size=Size}} -> 769: check_complete2(File,Size) 770: end. 771: 772: check_complete2(File,Size) -> 773: timer:sleep(500), 774: case file:read_file_info(File) of 775: {ok,#file_info{size=Size}} -> 776: ok; 777: {ok,#file_info{size=OtherSize}} -> 778: check_complete2(File,OtherSize) 779: end. 780: 781: dos_dump(DataDir,Rel,Dump) -> 782: DosDumpName = filename:join(DataDir,dump_prefix(Rel)++"dos"), 783: Cmd = "unix2dos " ++ Dump ++ " > " ++ DosDumpName, 784: Port = open_port({spawn,Cmd},[exit_status]), 785: receive 786: {Port,{exit_status,0}} -> 787: [DosDumpName]; 788: {Port,{exit_status,_Error}} -> 789: ?t:comment("Couldn't run \'unix2dos\'"), 790: [] 791: end. 792: 793: rel_opt(Rel) -> 794: case Rel of 795: r9b -> [{erl,[{release,"r9b_patched"}]}]; 796: r9c -> [{erl,[{release,"r9c_patched"}]}]; 797: r10b -> [{erl,[{release,"r10b_patched"}]}]; 798: r11b -> [{erl,[{release,"r11b_patched"}]}]; 799: r12b -> [{erl,[{release,"r12b_patched"}]}]; 800: r13b -> [{erl,[{release,"r13b_patched"}]}]; 801: r14b -> [{erl,[{release,"r14b_latest"}]}]; %naming convention changed 802: r15b -> [{erl,[{release,"r15b_latest"}]}]; 803: current -> [] 804: end. 805: 806: dump_prefix(Rel) -> 807: case Rel of 808: r9b -> "r9b_dump."; 809: r9c -> "r9c_dump."; 810: r10b -> "r10b_dump."; 811: r11b -> "r11b_dump."; 812: r12b -> "r12b_dump."; 813: r13b -> "r13b_dump."; 814: r14b -> "r14b_dump."; 815: r15b -> "r15b_dump."; 816: current -> "r16b_dump." 817: end. 818: 819: compat_rel(Rel) -> 820: case Rel of 821: r9b -> "+R9 "; 822: r9c -> "+R9 "; 823: r10b -> "+R10 "; 824: r11b -> "+R11 "; 825: r12b -> "+R12 "; 826: r13b -> "+R13 "; 827: r14b -> "+R14 "; 828: r15b -> "+R15 "; 829: current -> "" 830: end.