%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2005-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
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
-module(emem_SUITE).

%%-define(line_trace, 1).

-export([init_per_suite/1, end_per_suite/1,
	 receive_and_save_trace/2, send_trace/2]).


-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, 
	 init_per_testcase/2, end_per_testcase/2]).

-export([live_node/1,
	 'sparc_sunos5.8_32b_emt2.0'/1,
	 'pc_win2000_32b_emt2.0'/1,
	 'pc.smp_linux2.2.19pre17_32b_emt2.0'/1,
	 'powerpc_darwin7.7.0_32b_emt2.0'/1,
	 'alpha_osf1v5.1_64b_emt2.0'/1,
	 'sparc_sunos5.8_64b_emt2.0'/1,
	 'sparc_sunos5.8_32b_emt1.0'/1,
	 'pc_win2000_32b_emt1.0'/1,
	 'powerpc_darwin7.7.0_32b_emt1.0'/1,
	 'alpha_osf1v5.1_64b_emt1.0'/1,
	 'sparc_sunos5.8_64b_emt1.0'/1]).

-include_lib("kernel/include/file.hrl").

-include_lib("test_server/include/test_server.hrl").

-define(DEFAULT_TIMEOUT, ?t:minutes(5)).

-define(EMEM_64_32_COMMENT,
	"64 bit trace; this build of emem can only handle 32 bit traces").

-record(emem_res, {nodename,
		   hostname,
		   pid,
		   start_time,
		   trace_version,
		   max_word_size,
		   word_size,
		   last_values,
		   maximum,
		   exit_code}).

%%
%%
%% Exported suite functions
%%
%%

suite() -> [{ct_hooks,[ts_install_cth]}].

all() -> 
    case is_debug_compiled() of
	true -> {skip, "Not run when debug compiled"};
	false -> test_cases()
    end.

groups() -> 
    [].

init_per_group(_GroupName, Config) ->
    Config.

end_per_group(_GroupName, Config) ->
    Config.


test_cases() -> 
    [live_node, 'sparc_sunos5.8_32b_emt2.0',
     'pc_win2000_32b_emt2.0',
     'pc.smp_linux2.2.19pre17_32b_emt2.0',
     'powerpc_darwin7.7.0_32b_emt2.0',
     'alpha_osf1v5.1_64b_emt2.0',
     'sparc_sunos5.8_64b_emt2.0',
     'sparc_sunos5.8_32b_emt1.0', 'pc_win2000_32b_emt1.0',
     'powerpc_darwin7.7.0_32b_emt1.0',
     'alpha_osf1v5.1_64b_emt1.0',
     'sparc_sunos5.8_64b_emt1.0'].

init_per_testcase(Case, Config) when is_list(Config) ->
    case maybe_skip(Config) of
	{skip, _}=Skip -> Skip;
	ok ->
	    Dog = ?t:timetrap(?DEFAULT_TIMEOUT),

	    %% Until emem is completely stable we run these tests in a working
	    %% directory with an ignore_core_files file which will make the
	    %% search for core files ignore cores generated by this suite.
	    ignore_cores:setup(?MODULE,
			       Case,
			       [{watchdog, Dog}, {testcase, Case} | Config])
    end.

end_per_testcase(_Case, Config) when is_list(Config) ->
    ignore_cores:restore(Config),
    Dog = ?config(watchdog, Config),
    ?t:timetrap_cancel(Dog),
    ok.

maybe_skip(Config) ->
    DataDir = ?config(data_dir, Config),
    case filelib:is_dir(DataDir) of
	false ->
	    {skip, "No data directory"};
	true ->
	    case ?config(emem, Config) of
		undefined ->
		    {skip, "emem not found"};
		_ ->
		    ok
	    end
    end.

init_per_suite(Config) when is_list(Config) ->
    BinDir = filename:join([code:lib_dir(tools), "bin"]),
    Target = erlang:system_info(system_architecture),
    Res = (catch begin 
		     case check_dir(filename:join([BinDir, Target])) of
			 not_found -> ok;
			 TDir ->
			     check_emem(TDir, purecov),
			     check_emem(TDir, purify),
			     check_emem(TDir, debug),
			     check_emem(TDir, opt)
		     end,
		     check_emem(BinDir, opt),
		     ""
		 end),
    Res ++ ignore_cores:init(Config).

end_per_suite(Config) when is_list(Config) ->
    Config1 = lists:keydelete(emem, 1, Config),
    Config2 = lists:keydelete(emem_comment, 1, Config1),
    ignore_cores:fini(Config2).

%%
%%
%% Test cases
%%
%%

live_node(doc) -> [];
live_node(suite) -> [];
live_node(Config) when is_list(Config) ->
    ?line {ok, EmuFlag, Port} = start_emem(Config),
    ?line Nodename = mk_nodename(Config),
    ?line {ok, Node} = start_node(Nodename, EmuFlag),
    ?line NP = spawn(Node,
		     fun () ->
			     receive go -> ok end,
			     I = spawn(fun () -> ignorer end),
			     GC = fun () ->
					  GCP = fun (P) ->
							garbage_collect(P)
						end,
					  lists:foreach(GCP, processes())
				  end,
			     Seq = fun () -> I ! lists:seq(1, 1000000) end,
			     spawn_link(Seq),
			     B1 = <<0:10000000>>,
			     spawn_link(Seq),
			     B2 = <<0:10000000>>,
			     spawn_link(Seq),
			     B3 = <<0:10000000>>,
			     I ! {B1, B2, B3},
			     GC(),
			     GC(),
			     GC()
		     end),
    ?line MRef = erlang:monitor(process, NP),
    NP ! go,
    ?line receive
	      {'DOWN', MRef, process, NP, Reason} ->
		  ?line spawn(Node, fun () -> halt(17) end),
		  ?line normal = Reason
	  end,
    ?line Res = get_emem_result(Port),
    ?line {ok, Hostname} = inet:gethostname(),
    ?line ShortHostname = short_hostname(Hostname),
    ?line {true, _} = has_prefix(Nodename, Res#emem_res.nodename),
    ?line ShortHostname = short_hostname(Res#emem_res.hostname),
    ?line Bits = case erlang:system_info(wordsize) of
		     4 -> ?line "32 bits";
		     8 -> ?line "64 bits"
		 end,
    ?line Bits = Res#emem_res.word_size,
    ?line "17" = Res#emem_res.exit_code,
    ?line emem_comment(Config).

'sparc_sunos5.8_32b_emt2.0'(doc) -> [];
'sparc_sunos5.8_32b_emt2.0'(suite) -> [];
'sparc_sunos5.8_32b_emt2.0'(Config) when is_list(Config) ->
    ?line Res = run_emem_on_casefile(Config),
    ?line "test_server" = Res#emem_res.nodename,
    ?line "gorbag" = Res#emem_res.hostname,
    ?line "17074" = Res#emem_res.pid,
    ?line "2005-01-14 17:28:37.881980" = Res#emem_res.start_time,
    ?line "2.0" = Res#emem_res.trace_version,
    ?line "32 bits" = Res#emem_res.word_size,
    ?line ["15",
	   "2665739", "8992", "548986", "16131", "539994",
	   "4334192", "1", "99", "15", "98",
	   "0", "0", "49", "0", "49"] = Res#emem_res.last_values, 
    ?line ["5972061", "9662",
	   "7987824", "5",
	   "2375680", "3"] = Res#emem_res.maximum,
    ?line "0" = Res#emem_res.exit_code,
    ?line emem_comment(Config).

'pc_win2000_32b_emt2.0'(doc) -> [];
'pc_win2000_32b_emt2.0'(suite) -> [];
'pc_win2000_32b_emt2.0'(Config) when is_list(Config) ->
    ?line Res = run_emem_on_casefile(Config),
    ?line "test_server" = Res#emem_res.nodename,
    ?line "E-788FCF5191B54" = Res#emem_res.hostname,
    ?line "504" = Res#emem_res.pid,
    ?line "2005-01-24 17:27:28.224000" = Res#emem_res.start_time,
    ?line "2.0" = Res#emem_res.trace_version,
    ?line "32 bits" = Res#emem_res.word_size,
    ?line ["11",
	   "2932575", "8615", "641087", "68924", "632472"]
	= Res#emem_res.last_values, 
    ?line ["5434206", "9285"] = Res#emem_res.maximum,
    ?line "0" = Res#emem_res.exit_code,
    ?line emem_comment(Config).

'pc.smp_linux2.2.19pre17_32b_emt2.0'(doc) -> [];
'pc.smp_linux2.2.19pre17_32b_emt2.0'(suite) -> [];
'pc.smp_linux2.2.19pre17_32b_emt2.0'(Config) when is_list(Config) ->
    ?line Res = run_emem_on_casefile(Config),
    ?line "test_server" = Res#emem_res.nodename,
    ?line "four-roses" = Res#emem_res.hostname,
    ?line "20689" = Res#emem_res.pid,
    ?line "2005-01-20 13:11:26.143077" = Res#emem_res.start_time,
    ?line "2.0" = Res#emem_res.trace_version,
    ?line "32 bits" = Res#emem_res.word_size,
    ?line ["49",
	   "2901817", "9011", "521610", "10875", "512599",
	   "5392096", "2", "120", "10", "118",
	   "0", "0", "59", "0", "59"] = Res#emem_res.last_values,
    ?line ["6182918", "9681",
	   "9062112", "6",
	   "2322432", "3"] = Res#emem_res.maximum,
    ?line "0" = Res#emem_res.exit_code,
    ?line emem_comment(Config).


'powerpc_darwin7.7.0_32b_emt2.0'(doc) -> [];
'powerpc_darwin7.7.0_32b_emt2.0'(suite) -> [];
'powerpc_darwin7.7.0_32b_emt2.0'(Config) when is_list(Config) ->
    ?line Res = run_emem_on_casefile(Config),
    ?line "test_server" = Res#emem_res.nodename,
    ?line "grima" = Res#emem_res.hostname,
    ?line "13021" = Res#emem_res.pid,
    ?line "2005-01-20 15:08:17.568668" = Res#emem_res.start_time,
    ?line "2.0" = Res#emem_res.trace_version,
    ?line "32 bits" = Res#emem_res.word_size,
    ?line ["9",
	   "2784323", "8641", "531105", "15893", "522464"]
	= Res#emem_res.last_values,
    ?line ["6150376", "9311"] = Res#emem_res.maximum,
    ?line "0" = Res#emem_res.exit_code,
    ?line emem_comment(Config).

'alpha_osf1v5.1_64b_emt2.0'(doc) -> [];
'alpha_osf1v5.1_64b_emt2.0'(suite) -> [];
'alpha_osf1v5.1_64b_emt2.0'(Config) when is_list(Config) ->
    ?line Res = run_emem_on_casefile(Config),
    ?line "test_server" = Res#emem_res.nodename,
    ?line "thorin" = Res#emem_res.hostname,
    ?line "224630" = Res#emem_res.pid,
    ?line "2005-01-20 22:38:01.299632" = Res#emem_res.start_time,
    ?line "2.0" = Res#emem_res.trace_version,
    ?line "64 bits" = Res#emem_res.word_size,
    ?line case Res#emem_res.max_word_size of
	      "32 bits" ->
		  ?line emem_comment(Config, ?EMEM_64_32_COMMENT);
	      "64 bits" ->
		  ?line ["22",
			 "6591992", "8625", "516785", "14805", "508160",
			 "11429184", "5", "127", "254", "122",
			 "0", "0", "61", "0", "61"] = Res#emem_res.last_values,
		  ?line ["7041775", "9295",
			 "11593024", "7",
			 "2097152", "3"] = Res#emem_res.maximum,
		  ?line "0" = Res#emem_res.exit_code,
		  ?line emem_comment(Config)
	  end.

'sparc_sunos5.8_64b_emt2.0'(doc) -> [];
'sparc_sunos5.8_64b_emt2.0'(suite) -> [];
'sparc_sunos5.8_64b_emt2.0'(Config) when is_list(Config) ->
    ?line Res = run_emem_on_casefile(Config),
    ?line "test_server" = Res#emem_res.nodename,
    ?line "gorbag" = Res#emem_res.hostname,
    ?line "10907" = Res#emem_res.pid,
    ?line "2005-01-20 13:48:34.677068" = Res#emem_res.start_time,
    ?line "2.0" = Res#emem_res.trace_version,
    ?line "64 bits" = Res#emem_res.word_size,
    ?line case Res#emem_res.max_word_size of
	      "32 bits" ->
		  ?line emem_comment(Config, ?EMEM_64_32_COMMENT);
	      "64 bits" ->
		  ?line ["16",
			 "5032887", "8657", "530635", "14316", "521978",
			 "8627140", "5", "139", "19", "134",
			 "0", "0", "67", "0", "67"] = Res#emem_res.last_values,
		  ?line ["11695070", "9324",
			 "16360388", "10",
			 "4136960", "3"] = Res#emem_res.maximum,
		  ?line "0" = Res#emem_res.exit_code,
		  ?line emem_comment(Config)
	  end.

'sparc_sunos5.8_32b_emt1.0'(doc) -> [];
'sparc_sunos5.8_32b_emt1.0'(suite) -> [];
'sparc_sunos5.8_32b_emt1.0'(Config) when is_list(Config) ->
    ?line Res = run_emem_on_casefile(Config),
    ?line "" = Res#emem_res.nodename,
    ?line "" = Res#emem_res.hostname,
    ?line "" = Res#emem_res.pid,
    ?line "" = Res#emem_res.start_time,
    ?line "1.0" = Res#emem_res.trace_version,
    ?line "32 bits" = Res#emem_res.word_size,
    ?line ["11",
	   "2558261", "8643", "560610", "15325", "551967"]
	= Res#emem_res.last_values,
    ?line ["2791121", "9317"] = Res#emem_res.maximum,
    ?line "0" = Res#emem_res.exit_code,
    ?line emem_comment(Config).

'pc_win2000_32b_emt1.0'(doc) -> [];
'pc_win2000_32b_emt1.0'(suite) -> [];
'pc_win2000_32b_emt1.0'(Config) when is_list(Config) ->
    ?line Res = run_emem_on_casefile(Config),
    ?line "" = Res#emem_res.nodename,
    ?line "" = Res#emem_res.hostname,
    ?line "" = Res#emem_res.pid,
    ?line "" = Res#emem_res.start_time,
    ?line "1.0" = Res#emem_res.trace_version,
    ?line "32 bits" = Res#emem_res.word_size,
    ?line ["6",
	   "2965248", "8614", "640897", "68903", "632283"]
	= Res#emem_res.last_values,
    ?line ["3147090", "9283"] = Res#emem_res.maximum,
    ?line "0" = Res#emem_res.exit_code,
    ?line emem_comment(Config).


'powerpc_darwin7.7.0_32b_emt1.0'(doc) -> [];
'powerpc_darwin7.7.0_32b_emt1.0'(suite) -> [];
'powerpc_darwin7.7.0_32b_emt1.0'(Config) when is_list(Config) ->
    ?line Res = run_emem_on_casefile(Config),
    ?line "" = Res#emem_res.nodename,
    ?line "" = Res#emem_res.hostname,
    ?line "" = Res#emem_res.pid,
    ?line "" = Res#emem_res.start_time,
    ?line "1.0" = Res#emem_res.trace_version,
    ?line "32 bits" = Res#emem_res.word_size,
    ?line ["8",
	   "2852991", "8608", "529662", "15875", "521054"]
	= Res#emem_res.last_values,
    ?line ["3173335", "9278"] = Res#emem_res.maximum,
    ?line "0" = Res#emem_res.exit_code,
    ?line emem_comment(Config).

'alpha_osf1v5.1_64b_emt1.0'(doc) -> [];
'alpha_osf1v5.1_64b_emt1.0'(suite) -> [];
'alpha_osf1v5.1_64b_emt1.0'(Config) when is_list(Config) ->
    ?line Res = run_emem_on_casefile(Config),
    ?line "" = Res#emem_res.nodename,
    ?line "" = Res#emem_res.hostname,
    ?line "" = Res#emem_res.pid,
    ?line "" = Res#emem_res.start_time,
    ?line "1.0" = Res#emem_res.trace_version,
    ?line "64 bits" = Res#emem_res.word_size,
    ?line case Res#emem_res.max_word_size of
	      "32 bits" ->
		  ?line emem_comment(Config, ?EMEM_64_32_COMMENT);
	      "64 bits" ->
		  ?line ["22",
			 "6820094", "8612", "515518", "14812", "506906"]
		      = Res#emem_res.last_values,
		  ?line ["7292413", "9282"] = Res#emem_res.maximum,
		  ?line "0" = Res#emem_res.exit_code,
		  ?line emem_comment(Config)
	  end.

'sparc_sunos5.8_64b_emt1.0'(doc) -> [];
'sparc_sunos5.8_64b_emt1.0'(suite) -> [];
'sparc_sunos5.8_64b_emt1.0'(Config) when is_list(Config) ->
    ?line Res = run_emem_on_casefile(Config),
    ?line "" = Res#emem_res.nodename,
    ?line "" = Res#emem_res.hostname,
    ?line "" = Res#emem_res.pid,
    ?line "" = Res#emem_res.start_time,
    ?line "1.0" = Res#emem_res.trace_version,
    ?line "64 bits" = Res#emem_res.word_size,
    ?line case Res#emem_res.max_word_size of
	      "32 bits" ->
		  ?line emem_comment(Config, ?EMEM_64_32_COMMENT);
	      "64 bits" ->
		  ?line ["15",
			 "4965746", "8234", "543940", "14443", "535706"]
		      = Res#emem_res.last_values,
		  ?line ["11697645", "8908"] = Res#emem_res.maximum,
		  ?line "0" = Res#emem_res.exit_code,
		  ?line emem_comment(Config)
	  end.

%%
%%
%% Auxiliary functions
%%
%%

receive_and_save_trace(PortNumber, FileName) when is_integer(PortNumber),
						  is_list(FileName) ->
    {ok, F} = file:open(FileName, [write, compressed]),
    {ok, LS} = gen_tcp:listen(PortNumber, [inet, {reuseaddr,true}, binary]),
    {ok, S} = gen_tcp:accept(LS),
    gen_tcp:close(LS),
    receive_loop(S,F).

receive_loop(Socket, File) ->
    receive
	{tcp, Socket, Data} ->
	    ok = file:write(File, Data),
	    receive_loop(Socket, File);
	{tcp_closed, Socket} ->
	    file:close(File),
	    ok;
	{tcp_error, Socket, Reason} ->
	    file:close(File),
	    {error, Reason}
    end.

send_trace({Host, PortNumber}, FileName) when is_list(Host),
					      is_integer(PortNumber),
					      is_list(FileName) ->
    ?line {ok, F} = file:open(FileName, [read, compressed]),
    ?line {ok, S} = gen_tcp:connect(Host, PortNumber, [inet,{packet, 0}]),
    ?line send_loop(S, F);
send_trace(EmuFlag, FileName) when is_list(EmuFlag),
				   is_list(FileName) ->
    ?line ["+Mit", IpAddrStr, PortNoStr] = string:tokens(EmuFlag, " :"),
    ?line send_trace({IpAddrStr, list_to_integer(PortNoStr)}, FileName).

send_loop(Socket, File) ->
    ?line case file:read(File, 128) of
	      {ok, Data} ->
		  ?line case gen_tcp:send(Socket, Data) of
			    ok -> ?line send_loop(Socket, File);
			    Error ->
				?line gen_tcp:close(Socket),
				?line file:close(File),
				Error
			end;
	      eof ->
		  ?line gen_tcp:close(Socket),
		  ?line file:close(File),
		  ?line ok;
	      Error ->
		  ?line gen_tcp:close(Socket),
		  ?line file:close(File),
		  ?line Error
	  end.

check_emem(Dir, Type) when is_atom(Type) ->
    ExeSuffix = case ?t:os_type() of
		    {win32, _} -> ".exe";
		    _ -> ""
		end,
    TypeSuffix = case Type of
		     opt -> "";
		     _ -> "." ++ atom_to_list(Type)
		 end,
    Emem = "emem" ++ TypeSuffix ++ ExeSuffix,
    case check_file(filename:join([Dir, Emem])) of
	not_found -> ok;
	File ->
	    Comment = case Type of
			  opt -> "";
			  _ -> "[emem " ++ atom_to_list(Type) ++ " compiled]"
		      end,
	    throw([{emem, File}, {emem_comment, Comment}])
    end.

check_dir(DirName) ->
    case file:read_file_info(DirName) of
	{ok, #file_info {type = directory, access = A}} when A == read;
							     A == read_write ->
	    DirName;
	_ ->
	    not_found
    end.

check_file(FileName) ->
    case file:read_file_info(FileName) of
	{ok, #file_info {type = regular, access = A}} when A == read;
							   A == read_write ->
	    ?line FileName;
	_ ->
	    ?line not_found
    end.

emem_comment(Config) when is_list(Config) ->
    emem_comment(Config, "").

emem_comment(Config, ExtraComment)
  when is_list(Config), is_list(ExtraComment) ->
    case {?config(emem_comment, Config), ExtraComment} of
	{"", ""} -> ?line ok;
	{"", XC} -> ?line {comment, XC};
	{EmemC, ""} -> ?line {comment, EmemC};
	{EmemC, XC} -> ?line {comment, EmemC ++ " " ++ XC}
    end.

run_emem_on_casefile(Config) ->
    CaseName = atom_to_list(?config(testcase, Config)),
    ?line File = filename:join([?config(data_dir, Config), CaseName ++ ".gz"]),
    ?line case check_file(File) of
	      not_found ->
		  ?line ?t:fail({error, {filenotfound, File}});
	      _ ->
		  ?line ok
	  end,
    ?line {ok, EmuFlag, Port} = start_emem(Config),
    ?line Parent = self(),
    ?line Ref = make_ref(),
    ?line spawn_link(fun () ->
			     SRes = send_trace(EmuFlag, File),
			     Parent ! {Ref, SRes}
		     end),
    ?line Res = get_emem_result(Port),
    ?line receive
	      {Ref, ok} ->
		  ?line ok;
	      {Ref, SendError} ->
		  ?line ?t:format("Send result: ~p~n", [SendError])
	  end,
    ?line Res.

get_emem_result(Port) ->
    ?line {Res, LV} = get_emem_result(Port, {#emem_res{}, []}),
    ?line Res#emem_res{last_values = string:tokens(LV, " ")}.

get_emem_result(Port, {_EmemRes, _LastValues} = Res) ->
    ?line case get_emem_line(Port) of
	      eof ->
		  ?line Res;
	      Line ->
		  ?line get_emem_result(Port, parse_emem_line(Line, Res))
	  end.

parse_emem_main_header_footer_line(Line, {ER, LV} = Res) ->

    %% Header
    ?line case has_prefix("> Nodename:", Line) of
	      {true, NN} ->
		  ?line throw({ER#emem_res{nodename = strip(NN)}, LV});
	      false -> ?line ok
	  end,
    ?line case has_prefix("> Hostname:", Line) of
	      {true, HN} ->
		  ?line throw({ER#emem_res{hostname = strip(HN)}, LV});
	      false -> ?line ok
	  end,
    ?line case has_prefix("> Pid:", Line) of
	      {true, P} ->
		  ?line throw({ER#emem_res{pid = strip(P)}, LV});
	      false -> ?line ok
	  end,
    ?line case has_prefix("> Start time (UTC):", Line) of
	      {true, ST} ->
		  ?line throw({ER#emem_res{start_time = strip(ST)}, LV});
	      false -> ?line ok
	  end,
    ?line case has_prefix("> Actual trace version:", Line) of
	      {true, TV} ->
		  ?line throw({ER#emem_res{trace_version = strip(TV)}, LV});
	      false -> ?line ok
	  end,
    ?line case has_prefix("> Maximum trace word size:", Line) of
	      {true, MWS} ->
		  ?line throw({ER#emem_res{max_word_size = strip(MWS)}, LV});
	      false -> ?line ok
	  end,
    ?line case has_prefix("> Actual trace word size:", Line) of
	      {true, WS} ->
		  ?line throw({ER#emem_res{word_size = strip(WS)}, LV});
	      false -> ?line ok
	  end,

    %% Footer
    ?line case has_prefix("> Maximum:", Line) of
	      {true, M} ->
		  ?line throw({ER#emem_res{maximum = string:tokens(M," ")}, LV});
	      false -> ?line ok
	  end,
    ?line case has_prefix("> Emulator exited with code:", Line) of
	      {true, EC} ->
		  ?line throw({ER#emem_res{exit_code = strip(EC)}, LV});
	      false -> ?line ok
	  end,
    ?line Res.

parse_emem_header_line(_Line, {_ER, _LV} = Res) ->
    ?line Res.
    
parse_emem_value_line(Line, {EmemRes, _OldLastValues}) ->
    ?line {EmemRes, Line}.

parse_emem_line("", Res) ->
    ?line Res;
parse_emem_line(Line, Res) ->
    ?line [Prefix | _] = Line,
    case Prefix of
	$> -> ?line catch parse_emem_main_header_footer_line(Line, Res);
	$| -> ?line catch parse_emem_header_line(Line, Res);
	_ -> ?line catch parse_emem_value_line(Line, Res)
    end.

start_emem(Config) when is_list(Config) ->
    ?line Emem = ?config(emem, Config),
    ?line Cd = case ignore_cores:dir(Config) of
		   false -> [];
		   Dir -> [{cd, Dir}]
	       end,
    ?line case open_port({spawn, Emem ++ " -t -n -o -i 1"},
			 Cd ++ [{line, 1024}, eof]) of
	      Port when is_port(Port) -> ?line {ok, read_emu_flag(Port), Port};
	      Error -> ?line ?t:fail(Error)
	  end.

read_emu_flag(Port) ->
    ?line Line = case get_emem_line(Port) of
		     eof -> ?line ?t:fail(unexpected_end_of_file);
		     L -> ?line L
		 end,
    ?line case has_prefix("> Emulator command line argument:", Line) of
	      {true, EmuFlag} -> EmuFlag;
	      false -> ?line read_emu_flag(Port)
	  end.

get_emem_line(Port, Acc) ->
    ?line receive
	      {Port, {data, {eol, Data}}} ->
		  ?line Res = case Acc of
				  [] -> ?line Data;
				  _ -> ?line lists:flatten([Acc|Data])
			      end,
		  ?line ?t:format("~s", [Res]),
		  ?line Res;
	      {Port, {data, {noeol, Data}}} ->
		  ?line get_emem_line(Port, [Acc|Data]);
	      {Port, eof} ->
		  ?line port_close(Port),
		  ?line eof
	  end.

get_emem_line(Port) ->
    ?line get_emem_line(Port, []).

short_hostname([]) ->
    [];
short_hostname([$.|_]) ->
    [];
short_hostname([C|Cs]) ->
    [C | short_hostname(Cs)].

has_prefix([], List) when is_list(List) ->
    {true, List};
has_prefix([P|Xs], [P|Ys]) ->
    has_prefix(Xs, Ys);
has_prefix(_, _) ->
    false.

strip(Str) -> string:strip(Str).
    
mk_nodename(Config) ->
    {A, B, C} = now(),
    atom_to_list(?MODULE)
	++ "-" ++ atom_to_list(?config(testcase, Config))
	++ "-" ++ integer_to_list(A*1000000000000 + B*1000000 + C).

start_node(Name, Args) ->
    ?line Pa = filename:dirname(code:which(?MODULE)),
    ?line ?t:start_node(Name, peer, [{args, Args ++ " -pa " ++ Pa}]).

% stop_node(Node) ->
%     ?t:stop_node(Node).

is_debug_compiled() -> 
is_debug_compiled(erlang:system_info(system_version)).

is_debug_compiled([$d,$e,$b,$u,$g | _]) ->
    true;
is_debug_compiled([ _, _, _, _]) ->
    false;
is_debug_compiled([]) ->
    false;
is_debug_compiled([_|Rest]) ->
    is_debug_compiled(Rest).