aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/emulator/hipe/hipe_bif0.c10
-rw-r--r--erts/emulator/hipe/hipe_x86.c4
-rw-r--r--lib/eldap/test/eldap_basic_SUITE.erl132
-rw-r--r--lib/hipe/icode/Makefile2
-rw-r--r--lib/hipe/icode/hipe_icode_call_elim.erl78
-rw-r--r--lib/hipe/llvm/Makefile16
-rw-r--r--lib/hipe/llvm/elf_format.erl654
-rw-r--r--lib/hipe/llvm/elf_format.hrl40
-rw-r--r--lib/hipe/llvm/hipe_llvm.erl56
-rw-r--r--lib/hipe/llvm/hipe_llvm_main.erl189
-rw-r--r--lib/hipe/llvm/hipe_rtl_to_llvm.erl23
-rw-r--r--lib/hipe/main/hipe.app.src1
-rw-r--r--lib/hipe/main/hipe.erl55
-rw-r--r--lib/hipe/main/hipe_main.erl14
-rw-r--r--lib/hipe/misc/hipe_consttab.erl13
-rw-r--r--lib/hipe/misc/hipe_consttab.hrl2
-rw-r--r--lib/hipe/test/Makefile5
-rw-r--r--lib/hipe/test/hipe_testsuite_driver.erl24
-rw-r--r--lib/hipe/test/opt_verify_SUITE.erl62
-rw-r--r--lib/hipe/test/opt_verify_SUITE_data/call_elim_test.erl12
-rw-r--r--lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_no_opt_poss.erl32
-rw-r--r--lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_opt_poss.erl32
-rw-r--r--lib/kernel/doc/src/inet.xml7
-rw-r--r--lib/kernel/src/inet.erl7
-rw-r--r--lib/observer/doc/src/observer_ug.xml111
-rw-r--r--lib/observer/doc/src/ttb.xml56
-rw-r--r--lib/observer/src/Makefile1
-rw-r--r--lib/observer/src/observer.app.src1
-rw-r--r--lib/observer/src/observer_app_wx.erl8
-rw-r--r--lib/observer/src/observer_port_wx.erl479
-rw-r--r--lib/observer/src/observer_pro_wx.erl61
-rw-r--r--lib/observer/src/observer_trace_wx.erl672
-rw-r--r--lib/observer/src/observer_traceoptions_wx.erl106
-rw-r--r--lib/observer/src/observer_tv_wx.erl34
-rw-r--r--lib/observer/src/observer_wx.erl40
-rw-r--r--lib/observer/src/ttb.erl51
-rw-r--r--lib/observer/test/observer_SUITE.erl26
-rw-r--r--lib/runtime_tools/src/dbg.erl5
-rw-r--r--lib/runtime_tools/src/observer_backend.erl12
-rw-r--r--lib/ssh/src/ssh_message.erl10
-rw-r--r--lib/ssh/test/ssh_algorithms_SUITE.erl57
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE.erl19
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl86
-rw-r--r--lib/ssh/test/ssh_test_lib.erl53
-rw-r--r--system/doc/efficiency_guide/advanced.xml18
45 files changed, 2392 insertions, 984 deletions
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index 4063cbf306..58b5be3906 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -418,6 +418,8 @@ BIF_RETTYPE hipe_bifs_enter_code_2(BIF_ALIST_2)
BIF_RET(make_tuple(hp));
}
+#define IS_POWER_OF_TWO(Val) (((Val) > 0) && (((Val) & ((Val)-1)) == 0))
+
/*
* Allocate memory for arbitrary non-Erlang data.
*/
@@ -427,16 +429,18 @@ BIF_RETTYPE hipe_bifs_alloc_data_2(BIF_ALIST_2)
void *block;
if (is_not_small(BIF_ARG_1) || is_not_small(BIF_ARG_2) ||
- (align = unsigned_val(BIF_ARG_1),
- align != sizeof(long) && align != sizeof(double)))
+ (align = unsigned_val(BIF_ARG_1), !IS_POWER_OF_TWO(align)))
BIF_ERROR(BIF_P, BADARG);
nrbytes = unsigned_val(BIF_ARG_2);
if (nrbytes == 0)
BIF_RET(make_small(0));
block = erts_alloc(ERTS_ALC_T_HIPE, nrbytes);
- if ((unsigned long)block & (align-1))
+ if ((unsigned long)block & (align-1)) {
fprintf(stderr, "%s: erts_alloc(%lu) returned %p which is not %lu-byte aligned\r\n",
__FUNCTION__, (unsigned long)nrbytes, block, (unsigned long)align);
+ erts_free(ERTS_ALC_T_HIPE, block);
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+ }
BIF_RET(address_to_term(block, BIF_P));
}
diff --git a/erts/emulator/hipe/hipe_x86.c b/erts/emulator/hipe/hipe_x86.c
index 3d25646231..5f6c8c200e 100644
--- a/erts/emulator/hipe/hipe_x86.c
+++ b/erts/emulator/hipe/hipe_x86.c
@@ -37,7 +37,7 @@
void hipe_patch_load_fe(Uint32 *address, Uint32 value)
{
/* address points to a disp32 or imm32 operand */
- *address = value;
+ *address += value;
}
int hipe_patch_insn(void *address, Uint32 value, Eterm type)
@@ -54,7 +54,7 @@ int hipe_patch_insn(void *address, Uint32 value, Eterm type)
default:
return -1;
}
- *(Uint32*)address = value;
+ *(Uint32*)address += value;
return 0;
}
diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl
index 536e891a1e..d52a7c83f7 100644
--- a/lib/eldap/test/eldap_basic_SUITE.erl
+++ b/lib/eldap/test/eldap_basic_SUITE.erl
@@ -28,12 +28,11 @@
-include_lib("eldap/ebin/ELDAPv3.hrl").
--define(TIMEOUT, 120000). % 2 min
-
-
%% Control to delete a referral object:
-define(manageDsaIT, {control,"2.16.840.1.113730.3.4.2",false,asn1_NOVALUE}).
+suite() ->
+ [{timetrap,{seconds,40}}].
all() ->
[app,
@@ -124,7 +123,7 @@ end_per_suite(_Config) ->
init_per_group(return_values, Config) ->
- case ?config(ldap_server,Config) of
+ case proplists:get_value(ldap_server,Config) of
undefined ->
{skip, "LDAP server not availble"};
{Host,Port} ->
@@ -132,7 +131,7 @@ init_per_group(return_values, Config) ->
Config
end;
init_per_group(plain_api, Config0) ->
- case ?config(ldap_server,Config0) of
+ case proplists:get_value(ldap_server,Config0) of
undefined ->
{skip, "LDAP server not availble"};
Server = {Host,Port} ->
@@ -140,7 +139,7 @@ init_per_group(plain_api, Config0) ->
initialize_db([{server,Server}, {ssl_flag,false}, {start_tls,false} | Config0])
end;
init_per_group(ssl_api, Config0) ->
- case ?config(ldaps_server,Config0) of
+ case proplists:get_value(ldaps_server,Config0) of
undefined ->
{skip, "LDAPS server not availble"};
Server = {Host,Port} ->
@@ -148,7 +147,7 @@ init_per_group(ssl_api, Config0) ->
initialize_db([{server,Server}, {ssl_flag,true}, {start_tls,false} | Config0])
end;
init_per_group(start_tls_api, Config0) ->
- case {?config(ldap_server,Config0), ?config(ssl_available,Config0)} of
+ case {proplists:get_value(ldap_server,Config0), proplists:get_value(ssl_available,Config0)} of
{undefined,true} ->
{skip, "LDAP server not availble"};
{_,false} ->
@@ -187,18 +186,18 @@ end_per_group(_Group, Config) -> Config.
init_per_testcase(ssl_connection, Config) ->
- case ?config(ssl_available,Config) of
+ case proplists:get_value(ssl_available,Config) of
true ->
SSL_Port = 9999,
- CertFile = filename:join(?config(data_dir,Config), "certs/server/cert.pem"),
- KeyFile = filename:join(?config(data_dir,Config), "certs/server/key.pem"),
+ CertFile = filename:join(proplists:get_value(data_dir,Config), "certs/server/cert.pem"),
+ KeyFile = filename:join(proplists:get_value(data_dir,Config), "certs/server/key.pem"),
Parent = self(),
Listener = spawn_link(
fun() ->
case ssl:listen(SSL_Port, [{certfile, CertFile},
{keyfile, KeyFile}
- | ?config(tcp_listen_opts,Config)
+ | proplists:get_value(tcp_listen_opts,Config)
]) of
{ok,SSL_LSock} ->
Parent ! {ok,self()},
@@ -245,7 +244,7 @@ init_per_testcase(TC, Config) ->
end;
false ->
- case proplists:get_value(name,?config(tc_group_properties, Config)) of
+ case proplists:get_value(name,proplists:get_value(tc_group_properties, Config)) of
api_not_bound ->
{ok,H} = open(Config),
[{handle,H} | Config];
@@ -282,7 +281,7 @@ appup(Config) when is_list(Config) ->
%%%----------------------------------------------------------------
open_ret_val_success(Config) ->
- {Host,Port} = ?config(ldap_server,Config),
+ {Host,Port} = proplists:get_value(ldap_server,Config),
{ok,H} = eldap:open([Host], [{port,Port}]),
catch eldap:close(H).
@@ -292,7 +291,7 @@ open_ret_val_error(_Config) ->
%%%----------------------------------------------------------------
close_ret_val(Config) ->
- {Host,Port} = ?config(ldap_server,Config),
+ {Host,Port} = proplists:get_value(ldap_server,Config),
{ok,H} = eldap:open([Host], [{port,Port}]),
ok = eldap:close(H).
@@ -313,7 +312,6 @@ tcp_connection(Config) ->
end.
%%%----------------------------------------------------------------
-
close_after_tcp_error(Config) ->
Host = proplists:get_value(listen_host, Config),
Port = proplists:get_value(listen_port, Config),
@@ -436,37 +434,37 @@ tcp_connection_option(Config) ->
%%%----------------------------------------------------------------
elementary_search(Config) ->
{ok, #eldap_search_result{entries=[_]}} =
- eldap:search(?config(handle,Config),
- #eldap_search{base = ?config(eldap_path, Config),
+ eldap:search(proplists:get_value(handle,Config),
+ #eldap_search{base = proplists:get_value(eldap_path, Config),
filter= eldap:present("objectclass"),
scope = eldap:wholeSubtree()}).
%%%----------------------------------------------------------------
search_non_existant(Config) ->
{error, noSuchObject} =
- eldap:search(?config(handle,Config),
- #eldap_search{base = "cn=Bar," ++ ?config(eldap_path, Config),
+ eldap:search(proplists:get_value(handle,Config),
+ #eldap_search{base = "cn=Bar," ++ proplists:get_value(eldap_path, Config),
filter= eldap:present("objectclass"),
scope = eldap:wholeSubtree()}).
%%%----------------------------------------------------------------
add_when_not_bound(Config) ->
- {error, _} = eldap:add(?config(handle,Config),
- "cn=Jonas Jonsson," ++ ?config(eldap_path, Config),
+ {error, _} = eldap:add(proplists:get_value(handle,Config),
+ "cn=Jonas Jonsson," ++ proplists:get_value(eldap_path, Config),
[{"objectclass", ["person"]},
{"cn", ["Jonas Jonsson"]},
{"sn", ["Jonsson"]}]).
%%%----------------------------------------------------------------
bind(Config) ->
- ok = eldap:simple_bind(?config(handle,Config),
+ ok = eldap:simple_bind(proplists:get_value(handle,Config),
"cn=Manager,dc=ericsson,dc=se",
"hejsan").
%%%----------------------------------------------------------------
add_when_bound(Config) ->
- ok = eldap:add(?config(handle, Config),
- "cn=Jonas Jonsson," ++ ?config(eldap_path, Config),
+ ok = eldap:add(proplists:get_value(handle, Config),
+ "cn=Jonas Jonsson," ++ proplists:get_value(eldap_path, Config),
[{"objectclass", ["person"]},
{"cn", ["Jonas Jonsson"]},
{"sn", ["Jonsson"]}]).
@@ -474,16 +472,16 @@ add_when_bound(Config) ->
%%%----------------------------------------------------------------
add_already_exists(Config) ->
{error, entryAlreadyExists} =
- eldap:add(?config(handle, Config),
- "cn=Jonas Jonsson," ++ ?config(eldap_path, Config),
+ eldap:add(proplists:get_value(handle, Config),
+ "cn=Jonas Jonsson," ++ proplists:get_value(eldap_path, Config),
[{"objectclass", ["person"]},
{"cn", ["Jonas Jonsson"]},
{"sn", ["Jonsson"]}]).
%%%----------------------------------------------------------------
more_add(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ok = eldap:add(H, "cn=Foo Bar," ++ BasePath,
[{"objectclass", ["person"]},
{"cn", ["Foo Bar"]},
@@ -495,8 +493,8 @@ more_add(Config) ->
%%%----------------------------------------------------------------
add_referral(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
{ok,{referral,["ldap://nowhere.example.com"++_]}} =
eldap:add(H, "cn=Foo Bar,dc=notHere," ++ BasePath,
[{"objectclass", ["person"]},
@@ -506,28 +504,28 @@ add_referral(Config) ->
%%%----------------------------------------------------------------
search_filter_equalityMatch(Config) ->
- BasePath = ?config(eldap_path, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ExpectedDN = "cn=Jonas Jonsson," ++ BasePath,
{ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} =
- eldap:search(?config(handle, Config),
+ eldap:search(proplists:get_value(handle, Config),
#eldap_search{base = BasePath,
filter = eldap:equalityMatch("sn", "Jonsson"),
scope=eldap:singleLevel()}).
%%%----------------------------------------------------------------
search_filter_substring_any(Config) ->
- BasePath = ?config(eldap_path, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ExpectedDN = "cn=Jonas Jonsson," ++ BasePath,
{ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} =
- eldap:search(?config(handle, Config),
+ eldap:search(proplists:get_value(handle, Config),
#eldap_search{base = BasePath,
filter = eldap:substrings("sn", [{any, "ss"}]),
scope=eldap:singleLevel()}).
%%%----------------------------------------------------------------
search_filter_initial(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ExpectedDN = "cn=Foo Bar," ++ BasePath,
{ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} =
eldap:search(H,
@@ -537,8 +535,8 @@ search_filter_initial(Config) ->
%%%----------------------------------------------------------------
search_filter_final(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ExpectedDN = "cn=Foo Bar," ++ BasePath,
{ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} =
eldap:search(H,
@@ -548,8 +546,8 @@ search_filter_final(Config) ->
%%%----------------------------------------------------------------
search_filter_and(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ExpectedDN = "cn=Foo Bar," ++ BasePath,
{ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} =
eldap:search(H,
@@ -560,8 +558,8 @@ search_filter_and(Config) ->
%%%----------------------------------------------------------------
search_filter_or(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ExpectedDNs = lists:sort(["cn=Foo Bar," ++ BasePath,
"ou=Team," ++ BasePath]),
{ok, #eldap_search_result{entries=Es}} =
@@ -574,8 +572,8 @@ search_filter_or(Config) ->
%%%----------------------------------------------------------------
search_filter_and_not(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
{ok, #eldap_search_result{entries=[]}} =
eldap:search(H,
#eldap_search{base = BasePath,
@@ -587,8 +585,8 @@ search_filter_and_not(Config) ->
%%%----------------------------------------------------------------
search_two_hits(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
DN1 = "cn=Santa Claus," ++ BasePath,
DN2 = "cn=Jultomten," ++ BasePath,
%% Add two objects:
@@ -619,8 +617,8 @@ search_two_hits(Config) ->
%%%----------------------------------------------------------------
search_referral(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
DN = "cn=Santa Claus,dc=notHere," ++ BasePath,
{ok,{referral,["ldap://nowhere.example.com"++_]}} =
eldap:search(H, #eldap_search{base = DN,
@@ -629,8 +627,8 @@ search_referral(Config) ->
%%%----------------------------------------------------------------
modify(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
%% The object to modify
DN = "cn=Foo Bar," ++ BasePath,
@@ -662,8 +660,8 @@ modify(Config) ->
%%%----------------------------------------------------------------
modify_referral(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
%% The object to modify
DN = "cn=Foo Bar,dc=notHere," ++ BasePath,
@@ -675,8 +673,8 @@ modify_referral(Config) ->
%%%----------------------------------------------------------------
delete(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
%% The element to play with:
DN = "cn=Jonas Jonsson," ++ BasePath,
@@ -693,16 +691,16 @@ delete(Config) ->
%%%----------------------------------------------------------------
delete_referral(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
%% The element to play with:
DN = "cn=Jonas Jonsson,dc=notHere," ++ BasePath,
{ok,{referral,["ldap://nowhere.example.com"++_]}} = eldap:delete(H, DN).
%%%----------------------------------------------------------------
modify_dn_delete_old(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
OrigCN = "Foo Bar",
OriginalRDN = "cn="++OrigCN,
DN = OriginalRDN ++ "," ++ BasePath,
@@ -747,8 +745,8 @@ modify_dn_delete_old(Config) ->
%%%----------------------------------------------------------------
modify_dn_keep_old(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
OriginalRDN = "cn=Foo Bar",
DN = OriginalRDN ++ "," ++ BasePath,
NewCN = "Niclas Andre",
@@ -887,7 +885,7 @@ initialize_db(Config) ->
clear_db(Config) ->
{ok,H} = open_bind(Config),
- Path = ?config(eldap_path, Config),
+ Path = proplists:get_value(eldap_path, Config),
delete_old_contents(H, Path),
eldap:close(H),
Config.
@@ -939,20 +937,20 @@ ok(MODULE, LINE, X) ->
cond_start_tls(H, Config) ->
- case ?config(start_tls,Config) of
+ case proplists:get_value(start_tls,Config) of
true -> start_tls(H,Config);
_ -> Config
end.
start_tls(H, Config) ->
- KeyFile = filename:join([?config(data_dir,Config),
+ KeyFile = filename:join([proplists:get_value(data_dir,Config),
"certs/client/key.pem"
]),
case eldap:start_tls(H, [{keyfile, KeyFile}]) of
ok ->
[{start_tls_success,true} | Config];
Error ->
- ct:log("Start_tls on ~p failed: ~p",[?config(url,Config) ,Error]),
+ ct:log("Start_tls on ~p failed: ~p",[proplists:get_value(url,Config) ,Error]),
ct:fail("start_tls failed")
end.
@@ -964,8 +962,8 @@ open_bind(Config) ->
{ok,H}.
open(Config) ->
- {Host,Port} = ?config(server,Config),
- SSLflag = ?config(ssl_flag,Config),
+ {Host,Port} = proplists:get_value(server,Config),
+ SSLflag = proplists:get_value(ssl_flag,Config),
{ok,H} = eldap:open([Host], [{port,Port},{ssl,SSLflag}]),
cond_start_tls(H, Config),
{ok,H}.
@@ -1023,7 +1021,7 @@ init_ssl_certs_et_al(Config) ->
of
R when R==ok ; R=={error,{already_started,ssl}} ->
try make_certs:all("/dev/null",
- filename:join(?config(data_dir,Config), "certs"))
+ filename:join(proplists:get_value(data_dir,Config), "certs"))
of
{ok,_} -> true;
Other ->
diff --git a/lib/hipe/icode/Makefile b/lib/hipe/icode/Makefile
index a5edb10d90..c86562a981 100644
--- a/lib/hipe/icode/Makefile
+++ b/lib/hipe/icode/Makefile
@@ -59,7 +59,7 @@ DOC_MODULES = hipe_beam_to_icode \
hipe_icode_pp hipe_icode_primops \
hipe_icode_range \
hipe_icode_split_arith \
- hipe_icode_ssa hipe_icode_ssa_const_prop \
+ hipe_icode_ssa hipe_icode_ssa_const_prop hipe_icode_call_elim \
hipe_icode_ssa_copy_prop hipe_icode_ssa_struct_reuse \
hipe_icode_type $(HIPE_MODULES)
diff --git a/lib/hipe/icode/hipe_icode_call_elim.erl b/lib/hipe/icode/hipe_icode_call_elim.erl
new file mode 100644
index 0000000000..6a22133962
--- /dev/null
+++ b/lib/hipe/icode/hipe_icode_call_elim.erl
@@ -0,0 +1,78 @@
+%% -*- erlang-indent-level: 2 -*-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% File : hipe_icode_call_elim.erl
+%% Authors : Daniel S. McCain <[email protected]>,
+%% Magnus Lång <[email protected]>
+%% Created : 14 Apr 2014 by Magnus Lång <[email protected]>
+%% Purpose : Eliminate calls to BIFs that are side-effect free only when
+%% executed on some argument types.
+%%----------------------------------------------------------------------
+-module(hipe_icode_call_elim).
+-export([cfg/1]).
+
+-include("hipe_icode.hrl").
+-include("../flow/cfg.hrl").
+
+-spec cfg(cfg()) -> cfg().
+
+cfg(IcodeSSA) ->
+ lists:foldl(fun (Lbl, CFG1) ->
+ BB1 = hipe_icode_cfg:bb(CFG1, Lbl),
+ Code1 = hipe_bb:code(BB1),
+ Code2 = lists:map(fun elim_insn/1, Code1),
+ BB2 = hipe_bb:code_update(BB1, Code2),
+ hipe_icode_cfg:bb_add(CFG1, Lbl, BB2)
+ end, IcodeSSA, hipe_icode_cfg:labels(IcodeSSA)).
+
+-spec elim_insn(icode_instr()) -> icode_instr().
+elim_insn(Insn=#icode_call{'fun'={_,_,_}=MFA, args=Args, type=remote,
+ dstlist=[Dst=#icode_variable{
+ annotation={type_anno, RetType, _}}]}) ->
+ Opaques = 'universe',
+ case erl_types:t_is_singleton(RetType, Opaques) of
+ true ->
+ ArgTypes = [case Arg of
+ #icode_variable{annotation={type_anno, Type, _}} -> Type;
+ #icode_const{} ->
+ erl_types:t_from_term(hipe_icode:const_value(Arg))
+ end || Arg <- Args],
+ case can_be_eliminated(MFA, ArgTypes) of
+ true ->
+ Const = hipe_icode:mk_const(
+ erl_types:t_singleton_to_term(RetType, Opaques)),
+ #icode_move{dst=Dst, src=Const};
+ false -> Insn
+ end;
+ false -> Insn
+ end;
+elim_insn(Insn) -> Insn.
+
+
+%% A function can be eliminated for some argument types if it has no side
+%% effects when run on arguments of those types.
+
+-spec can_be_eliminated(mfa(), [erl_types:erl_type()]) -> boolean().
+
+can_be_eliminated({maps, is_key, 2}, [_K, M]) ->
+ erl_types:t_is_map(M);
+can_be_eliminated(_, _) ->
+ false.
diff --git a/lib/hipe/llvm/Makefile b/lib/hipe/llvm/Makefile
index d2d39fb9e3..25b47a580f 100644
--- a/lib/hipe/llvm/Makefile
+++ b/lib/hipe/llvm/Makefile
@@ -40,12 +40,12 @@ RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN)
# Target Specs
# ----------------------------------------------------
ifdef HIPE_ENABLED
-HIPE_MODULES = hipe_rtl_to_llvm \
+HIPE_MODULES = elf_format \
hipe_llvm \
- elf_format \
+ hipe_llvm_liveness \
hipe_llvm_main \
hipe_llvm_merge \
- hipe_llvm_liveness
+ hipe_rtl_to_llvm
else
HIPE_MODULES =
endif
@@ -71,7 +71,7 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
include ../native.mk
-ERL_COMPILE_FLAGS += +inline #+warn_missing_spec
+ERL_COMPILE_FLAGS += +inline +warn_export_vars #+warn_missing_spec
# if in 32 bit backend define BIT32 symbol
ARCH = $(shell echo $(TARGET) | sed 's/^\(x86_64\)-.*/64bit/')
@@ -108,3 +108,11 @@ release_spec: opt
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
release_docs_spec:
+
+$(EBIN)/elf_format.beam: elf_format.hrl elf32_format.hrl elf64_format.hrl
+$(EBIN)/hipe_llvm_main.beam: ../../kernel/src/hipe_ext_format.hrl \
+ hipe_llvm_arch.hrl elf_format.hrl elf32_format.hrl elf64_format.hrl
+$(EBIN)/hipe_llvm_merge.beam: ../../kernel/src/hipe_ext_format.hrl \
+ hipe_llvm_arch.hrl ../rtl/hipe_literals.hrl ../main/hipe.hrl
+$(EBIN)/hipe_rtl_to_llvm.beam: ../rtl/hipe_rtl.hrl ../rtl/hipe_literals.hrl \
+ hipe_llvm_arch.hrl
diff --git a/lib/hipe/llvm/elf_format.erl b/lib/hipe/llvm/elf_format.erl
index 260da9b5e6..8cf6ea6250 100644
--- a/lib/hipe/llvm/elf_format.erl
+++ b/lib/hipe/llvm/elf_format.erl
@@ -13,21 +13,20 @@
-module(elf_format).
--export([get_tab_entries/1,
- %% Relocations
- get_rodata_relocs/1,
- get_text_relocs/1,
+-export([%% Relocations
extract_rela/2,
- get_rela_addends/1,
%% Note
extract_note/2,
%% Executable code
extract_text/1,
%% GCC Exception Table
- get_exn_handlers/1,
- %% Misc.
- set_architecture_flag/1,
- is64bit/0
+ get_exn_handlers/1,
+ %% Symbols
+ elf_symbols/1,
+ %% Sections
+ section_contents/2,
+ %% Main interface
+ read/1
]).
-include("elf_format.hrl").
@@ -36,27 +35,57 @@
%% Types
%%------------------------------------------------------------------------------
--type elf() :: binary().
-
--type lp() :: non_neg_integer(). % landing pad
--type num() :: non_neg_integer().
--type index() :: non_neg_integer().
--type offset() :: non_neg_integer().
--type size() :: non_neg_integer().
--type start() :: non_neg_integer().
-
--type info() :: index().
--type nameoff() :: offset().
--type valueoff() :: offset().
-
--type name() :: string().
--type name_size() :: {name(), size()}.
--type name_sizes() :: [name_size()].
+-export_type([elf/0
+ ,addend/0
+ ,bitflags/0
+ ,name/0
+ ,offset/0
+ ,reloc_type/0
+ ,shdr_type/0
+ ,size/0
+ ,sym_bind/0
+ ,sym_type/0
+ ,valueoff/0
+ ]).
+
+-type bitflags() :: non_neg_integer().
+-type index() :: non_neg_integer().
+-type lp() :: non_neg_integer(). % landing pad
+-type num() :: non_neg_integer().
+-type offset() :: non_neg_integer().
+-type size() :: non_neg_integer().
+-type start() :: non_neg_integer().
+
+-type addend() :: integer() | undefined.
+-type name() :: string().
+-type shdr_type() :: 'null' | 'progbits' | 'symtab' | 'strtab' | 'rela'
+ | 'hash' | 'dynamic' | 'note' | 'nobits' | 'rel' | 'shlib'
+ | 'dynsym' | {os, ?SHT_LOOS..?SHT_HIOS}
+ | {proc, ?SHT_LOPROC..?SHT_HIPROC}.
+-type sym_bind() :: 'local' | 'global' | 'weak' | {os, ?STB_LOOS..?STB_HIOS}
+ | {proc, ?STB_LOPROC..?STB_HIPROC}.
+-type sym_type() :: 'notype' | 'object' | 'func' | 'section' | 'file'
+ | {os, ?STT_LOOS..?STT_HIOS}
+ | {proc, ?STT_LOPROC..?STT_HIPROC}.
+-type valueoff() :: offset().
+
+-ifdef(BIT32). % 386
+-type reloc_type() :: '32' | 'pc32'.
+-else. % X86_64
+-type reloc_type() :: '64' | 'pc32' | '32'.
+-endif.
%%------------------------------------------------------------------------------
%% Abstract Data Types and Accessors for ELF Structures.
%%------------------------------------------------------------------------------
+-record(elf, {file :: binary()
+ ,sections :: [elf_shdr()]
+ ,sec_nam :: #{string() => elf_shdr()}
+ ,symbols :: undefined | [elf_sym()]
+ }).
+-opaque elf() :: #elf{}.
+
%% File header
-record(elf_ehdr, {ident, % ELF identification
type, % Object file type
@@ -85,42 +114,6 @@
}).
%% -type elf_ehdr_ident() :: #elf_ehdr_ident{}.
-%% Section header entries
--record(elf_shdr, {name, % Section name
- type, % Section type
- flags, % Section attributes
- addr, % Virtual address in memory
- offset :: offset(), % Offset in file
- size :: size(), % Size of section
- link, % Link to other section
- info, % Miscellaneous information
- addralign, % Address align boundary
- entsize % Size of entries, if section has table
- }).
-%% -type elf_shdr() :: #elf_shdr{}.
-
-%% Symbol table entries
--record(elf_sym, {name :: nameoff(), % Symbol name
- info, % Type and Binding attributes
- other, % Reserved
- shndx, % Section table index
- value :: valueoff(), % Symbol value
- size :: size() % Size of object
- }).
--type elf_sym() :: #elf_sym{}.
-
-%% Relocations
--record(elf_rel, {r_offset :: offset(), % Address of reference
- r_info :: info() % Symbol index and type of relocation
- }).
--type elf_rel() :: #elf_rel{}.
-
--record(elf_rela, {r_offset :: offset(), % Address of reference
- r_info :: info(), % Symbol index and type of relocation
- r_addend :: offset() % Constant part of expression
- }).
--type elf_rela() :: #elf_rela{}.
-
%% %% Program header table
%% -record(elf_phdr, {type, % Type of segment
%% flags, % Segment attributes
@@ -199,44 +192,19 @@ mk_shdr(Name, Type, Flags, Addr, Offset, Size, Link, Info, AddrAlign, EntSize) -
%%%-------------------------
%%% Symbol Table Entries
%%%-------------------------
-mk_sym(Name, Info, Other, Shndx, Value, Size) ->
- #elf_sym{name = Name, info = Info, other = Other,
- shndx = Shndx, value = Value, size = Size}.
-
--spec sym_name(elf_sym()) -> nameoff().
-sym_name(#elf_sym{name = Name}) -> Name.
+mk_sym(Name, Bind, Type, Section, Value, Size) ->
+ #elf_sym{name = Name, bind = Bind, type = Type,
+ section = Section, value = Value, size = Size}.
+%% -spec sym_name(elf_sym()) -> string().
+%% sym_name(#elf_sym{name = Name}) -> Name.
+%%
%% -spec sym_value(elf_sym()) -> valueoff().
%% sym_value(#elf_sym{value = Value}) -> Value.
%%
%% -spec sym_size(elf_sym()) -> size().
%% sym_size(#elf_sym{size = Size}) -> Size.
-%%%-------------------------
-%%% Relocations
-%%%-------------------------
--spec mk_rel(offset(), info()) -> elf_rel().
-mk_rel(Offset, Info) ->
- #elf_rel{r_offset = Offset, r_info = Info}.
-
-%% The following two functions capitalize on the fact that the two kinds of
-%% relocation records (for 32- and 64-bit architectures have similar structure.
-
--spec r_offset(elf_rel() | elf_rela()) -> offset().
-r_offset(#elf_rel{r_offset = Offset}) -> Offset;
-r_offset(#elf_rela{r_offset = Offset}) -> Offset.
-
--spec r_info(elf_rel() | elf_rela()) -> info().
-r_info(#elf_rel{r_info = Info}) -> Info;
-r_info(#elf_rela{r_info = Info}) -> Info.
-
--spec mk_rela(offset(), info(), offset()) -> elf_rela().
-mk_rela(Offset, Info, Addend) ->
- #elf_rela{r_offset = Offset, r_info = Info, r_addend = Addend}.
-
--spec rela_addend(elf_rela()) -> offset().
-rela_addend(#elf_rela{r_addend = Addend}) -> Addend.
-
%% %%%-------------------------
%% %%% GCC exception table
%% %%%-------------------------
@@ -263,15 +231,30 @@ mk_gccexntab_callsite(Start, Size, LP, Action) ->
%% gccexntab_callsite_lp(#elf_gccexntab_callsite{lp = LP}) -> LP.
%%------------------------------------------------------------------------------
+%% Main interface function
+%%------------------------------------------------------------------------------
+
+%% @doc Parses an ELF file.
+-spec read(binary()) -> elf().
+read(ElfBin) ->
+ Header = extract_header(ElfBin),
+ [_UndefinedSec|Sections] = extract_shdrtab(ElfBin, Header),
+ SecNam = maps:from_list(
+ [{Name, Sec} || Sec = #elf_shdr{name=Name} <- Sections]),
+ Elf0 = #elf{file=ElfBin, sections=Sections, sec_nam=SecNam},
+ [_UndefinedSym|Symbols] = extract_symtab(Elf0, extract_strtab(Elf0)),
+ Elf0#elf{symbols=Symbols}.
+
+%%------------------------------------------------------------------------------
%% Functions to manipulate the ELF File Header
%%------------------------------------------------------------------------------
%% @doc Extracts the File Header from an ELF formatted object file. Also sets
%% the ELF class variable in the process dictionary (used by many functions
%% in this and hipe_llvm_main modules).
--spec extract_header(elf()) -> elf_ehdr().
-extract_header(Elf) ->
- Ehdr_bin = get_binary_segment(Elf, 0, ?ELF_EHDR_SIZE),
+-spec extract_header(binary()) -> elf_ehdr().
+extract_header(ElfBin) ->
+ Ehdr_bin = get_binary_segment(ElfBin, 0, ?ELF_EHDR_SIZE),
<< %% Structural pattern matching on fields.
Ident_bin:?E_IDENT_SIZE/binary,
Type:?bits(?E_TYPE_SIZE)/integer-little,
@@ -300,19 +283,28 @@ extract_header(Elf) ->
%% Functions to manipulate Section Header Entries
%%------------------------------------------------------------------------------
+-type shdrtab() :: [elf_shdr()].
+
%% @doc Extracts the Section Header Table from an ELF formated Object File.
-extract_shdrtab(Elf) ->
- %% Extract File Header to get info about Section Header Offset (in bytes),
- %% Entry Size (in bytes) and Number of entries
- #elf_ehdr{shoff = ShOff, shentsize = ShEntsize, shnum = ShNum} =
- extract_header(Elf),
+-spec extract_shdrtab(binary(), elf_ehdr()) -> shdrtab().
+extract_shdrtab(ElfBin, #elf_ehdr{shoff=ShOff, shentsize=?ELF_SHDRENTRY_SIZE,
+ shnum=ShNum, shstrndx=ShStrNdx}) ->
%% Get actual Section header table (binary)
- ShdrBin = get_binary_segment(Elf, ShOff, ShNum * ShEntsize),
- get_shdrtab_entries(ShdrBin, []).
-
-get_shdrtab_entries(<<>>, Acc) ->
- lists:reverse(Acc);
-get_shdrtab_entries(ShdrBin, Acc) ->
+ ShdrBin = get_binary_segment(ElfBin, ShOff, ShNum * ?ELF_SHDRENTRY_SIZE),
+ %% We need to lookup the offset and size of the section header string table
+ %% before we can fully parse the section table. We compute its offset and
+ %% extract the fields we need here.
+ ShStrEntryOffset = ShStrNdx * ?ELF_SHDRENTRY_SIZE,
+ <<_:ShStrEntryOffset/binary, _:?SH_NAME_SIZE/binary,
+ _:?SH_TYPE_SIZE/binary, _:?SH_FLAGS_SIZE/binary, _:?SH_ADDR_SIZE/binary,
+ ShStrOffset:?bits(?SH_OFFSET_SIZE)/little,
+ ShStrSize:?bits(?SH_SIZE_SIZE)/little,
+ _/binary>> = ShdrBin,
+ ShStrTab = parse_strtab(get_binary_segment(ElfBin, ShStrOffset, ShStrSize)),
+ get_shdrtab_entries(ShdrBin, ShStrTab).
+
+get_shdrtab_entries(<<>>, _ShStrTab) -> [];
+get_shdrtab_entries(ShdrTab, ShStrTab) ->
<<%% Structural pattern matching on fields.
Name:?bits(?SH_NAME_SIZE)/integer-little,
Type:?bits(?SH_TYPE_SIZE)/integer-little,
@@ -324,205 +316,166 @@ get_shdrtab_entries(ShdrBin, Acc) ->
Info:?bits(?SH_INFO_SIZE)/integer-little,
Addralign:?bits(?SH_ADDRALIGN_SIZE)/integer-little,
Entsize:?bits(?SH_ENTSIZE_SIZE)/integer-little,
- MoreShdrE/binary
- >> = ShdrBin,
- ShdrE = mk_shdr(Name, Type, Flags, Addr, Offset,
- Size, Link, Info, Addralign, Entsize),
- get_shdrtab_entries(MoreShdrE, [ShdrE | Acc]).
-
-%% @doc Extracts a specific Entry of a Section Header Table. This function
-%% takes as argument the Section Header Table (`SHdrTab') and the entry's
-%% serial number (`EntryNum') and returns the entry (`shdr').
-get_shdrtab_entry(SHdrTab, EntryNum) ->
- lists:nth(EntryNum + 1, SHdrTab).
-
-%%------------------------------------------------------------------------------
-%% Functions to manipulate Section Header String Table
-%%------------------------------------------------------------------------------
-
-%% @doc Extracts the Section Header String Table. This section is not a known
-%% ELF Object File section. It is just a "hidden" table storing the
-%% names of all sections that exist in current object file.
--spec extract_shstrtab(elf()) -> [name()].
-extract_shstrtab(Elf) ->
- %% Extract Section Name String Table Index
- #elf_ehdr{shstrndx = ShStrNdx} = extract_header(Elf),
- ShHdrTab = extract_shdrtab(Elf),
- %% Extract Section header entry and get actual Section-header String Table
- #elf_shdr{offset = ShStrOffset, size = ShStrSize} =
- get_shdrtab_entry(ShHdrTab, ShStrNdx),
- case get_binary_segment(Elf, ShStrOffset, ShStrSize) of
- <<>> -> %% Segment empty
- [];
- ShStrTab -> %% Convert to string table
- [Name || {Name, _Size} <- get_names(ShStrTab)]
- end.
-
-%%------------------------------------------------------------------------------
-
--spec get_tab_entries(elf()) -> [{name(), valueoff(), size()}].
-get_tab_entries(Elf) ->
- SymTab = extract_symtab(Elf),
- Ts = [{Name, Value, Size div ?ELF_XWORD_SIZE}
- || #elf_sym{name = Name, value = Value, size = Size} <- SymTab,
- Name =/= 0],
- {NameIndices, ValueOffs, Sizes} = lists:unzip3(Ts),
- %% Find the names of the symbols.
- %% Get string table entries ([{Name, Offset in strtab section}]). Keep only
- %% relevant entries:
- StrTab = extract_strtab(Elf),
- Relevant = [get_strtab_entry(StrTab, Off) || Off <- NameIndices],
- %% Zip back to {Name, ValueOff, Size}
- lists:zip3(Relevant, ValueOffs, Sizes).
+ Rest/binary
+ >> = ShdrTab,
+ Entry = mk_shdr(get_strtab_entry(Name, ShStrTab), decode_shdr_type(Type),
+ Flags, Addr, Offset, Size, Link, Info, Addralign, Entsize),
+ [Entry | get_shdrtab_entries(Rest, ShStrTab)].
+
+decode_shdr_type(?SHT_NULL) -> 'null';
+decode_shdr_type(?SHT_PROGBITS) -> 'progbits';
+decode_shdr_type(?SHT_SYMTAB) -> 'symtab';
+decode_shdr_type(?SHT_STRTAB) -> 'strtab';
+decode_shdr_type(?SHT_RELA) -> 'rela';
+decode_shdr_type(?SHT_HASH) -> 'hash'; %unused
+decode_shdr_type(?SHT_DYNAMIC) -> 'dynamic'; %unused
+decode_shdr_type(?SHT_NOTE) -> 'note'; %unused
+decode_shdr_type(?SHT_NOBITS) -> 'nobits';
+decode_shdr_type(?SHT_REL) -> 'rel';
+decode_shdr_type(?SHT_SHLIB) -> 'shlib'; %unused
+decode_shdr_type(?SHT_DYNSYM) -> 'dynsym'; %unused
+decode_shdr_type(OS) when ?SHT_LOOS =< OS, OS =< ?SHT_HIOS -> {os, OS};
+decode_shdr_type(Proc) when ?SHT_LOPROC =< Proc, Proc =< ?SHT_HIPROC ->
+ {proc, Proc}.
+
+-spec elf_section(non_neg_integer(), elf()) -> undefined | abs | elf_shdr().
+elf_section(0, #elf{}) -> undefined;
+elf_section(?SHN_ABS, #elf{}) -> abs;
+elf_section(Index, #elf{sections=SecIdx}) ->
+ lists:nth(Index, SecIdx).
+
+%% Reads the contents of a section from an object
+-spec section_contents(elf_shdr(), elf()) -> binary().
+section_contents(#elf_shdr{offset=Offset, size=Size}, #elf{file=ElfBin}) ->
+ get_binary_segment(ElfBin, Offset, Size).
%%------------------------------------------------------------------------------
%% Functions to manipulate Symbol Table
%%------------------------------------------------------------------------------
%% @doc Function that extracts Symbol Table from an ELF Object file.
-extract_symtab(Elf) ->
- Symtab_bin = extract_segment_by_name(Elf, ?SYMTAB),
- get_symtab_entries(Symtab_bin, []).
-
-get_symtab_entries(<<>>, Acc) ->
- lists:reverse(Acc);
-get_symtab_entries(Symtab_bin, Acc) ->
- <<SymE_bin:?ELF_SYM_SIZE/binary, MoreSymE/binary>> = Symtab_bin,
- case is64bit() of
- true ->
- <<%% Structural pattern matching on fields.
- Name:?bits(?ST_NAME_SIZE)/integer-little,
- Info:?bits(?ST_INFO_SIZE)/integer-little,
- Other:?bits(?ST_OTHER_SIZE)/integer-little,
- Shndx:?bits(?ST_SHNDX_SIZE)/integer-little,
- Value:?bits(?ST_VALUE_SIZE)/integer-little,
- Size:?bits(?ST_SIZE_SIZE)/integer-little
- >> = SymE_bin;
- false ->
- << %% Same fields in different order:
- Name:?bits(?ST_NAME_SIZE)/integer-little,
- Value:?bits(?ST_VALUE_SIZE)/integer-little,
- Size:?bits(?ST_SIZE_SIZE)/integer-little,
- Info:?bits(?ST_INFO_SIZE)/integer-little,
- Other:?bits(?ST_OTHER_SIZE)/integer-little,
- Shndx:?bits(?ST_SHNDX_SIZE)/integer-little
- >> = SymE_bin
- end,
- SymE = mk_sym(Name, Info, Other, Shndx, Value, Size),
- get_symtab_entries(MoreSymE, [SymE | Acc]).
-
-%% @doc Extracts a specific entry from the Symbol Table (as binary).
-%% This function takes as arguments the Symbol Table (`SymTab')
-%% and the entry's serial number and returns that entry (`sym').
-get_symtab_entry(SymTab, EntryNum) ->
- lists:nth(EntryNum + 1, SymTab).
+extract_symtab(Elf, StrTab) ->
+ Symtab = extract_segment_by_name(Elf, ?SYMTAB),
+ [parse_sym(Sym, Elf, StrTab) || <<Sym:?ELF_SYM_SIZE/binary>> <= Symtab].
+
+-ifdef(BIT32).
+parse_sym(<<%% Structural pattern matching on fields.
+ Name:?bits(?ST_NAME_SIZE)/integer-little,
+ Value:?bits(?ST_VALUE_SIZE)/integer-little,
+ Size:?bits(?ST_SIZE_SIZE)/integer-little,
+ Info:?bits(?ST_INFO_SIZE)/integer-little,
+ _Other:?bits(?ST_OTHER_SIZE)/integer-little,
+ Shndx:?bits(?ST_SHNDX_SIZE)/integer-little>>,
+ Elf, StrTab) ->
+ mk_sym(get_strtab_entry(Name, StrTab), decode_symbol_bind(?ELF_ST_BIND(Info)),
+ decode_symbol_type(?ELF_ST_TYPE(Info)), elf_section(Shndx, Elf), Value,
+ Size).
+-else.
+parse_sym(<<%% Same fields in different order:
+ Name:?bits(?ST_NAME_SIZE)/integer-little,
+ Info:?bits(?ST_INFO_SIZE)/integer-little,
+ _Other:?bits(?ST_OTHER_SIZE)/integer-little,
+ Shndx:?bits(?ST_SHNDX_SIZE)/integer-little,
+ Value:?bits(?ST_VALUE_SIZE)/integer-little,
+ Size:?bits(?ST_SIZE_SIZE)/integer-little>>,
+ Elf, StrTab) ->
+ mk_sym(get_strtab_entry(Name, StrTab), decode_symbol_bind(?ELF_ST_BIND(Info)),
+ decode_symbol_type(?ELF_ST_TYPE(Info)), elf_section(Shndx, Elf), Value,
+ Size).
+-endif.
+
+decode_symbol_bind(?STB_LOCAL) -> 'local';
+decode_symbol_bind(?STB_GLOBAL) -> 'global';
+decode_symbol_bind(?STB_WEAK) -> 'weak'; %unused
+decode_symbol_bind(OS) when ?STB_LOOS =< OS, OS =< ?STB_HIOS -> {os, OS};
+decode_symbol_bind(Proc) when ?STB_LOPROC =< Proc, Proc =< ?STB_HIPROC ->
+ {proc, Proc}.
+
+decode_symbol_type(?STT_NOTYPE) -> 'notype';
+decode_symbol_type(?STT_OBJECT) -> 'object';
+decode_symbol_type(?STT_FUNC) -> 'func';
+decode_symbol_type(?STT_SECTION) -> 'section';
+decode_symbol_type(?STT_FILE) -> 'file';
+decode_symbol_type(OS) when ?STT_LOOS =< OS, OS =< ?STT_HIOS -> {os, OS};
+decode_symbol_type(Proc) when ?STT_LOPROC =< Proc, Proc =< ?STT_HIPROC ->
+ {proc, Proc}.
+
+%% @doc Extracts a specific entry from the Symbol Table.
+-spec elf_symbol(0, elf()) -> undefined;
+ (pos_integer(), elf()) -> elf_sym().
+elf_symbol(0, #elf{}) -> undefined;
+elf_symbol(Index, #elf{symbols=Symbols}) -> lists:nth(Index, Symbols).
+
+-spec elf_symbols(elf()) -> [elf_sym()].
+elf_symbols(#elf{symbols=Symbols}) -> Symbols.
%%------------------------------------------------------------------------------
%% Functions to manipulate String Table
%%------------------------------------------------------------------------------
+%% ADT: get_strtab_entry/1 must be used to consume this type.
+-type strtab() :: binary().
+
%% @doc Extracts String Table from an ELF formated Object File.
--spec extract_strtab(elf()) -> [{string(), offset()}].
+-spec extract_strtab(elf()) -> strtab().
extract_strtab(Elf) ->
- Strtab_bin = extract_segment_by_name(Elf, ?STRTAB),
- NamesSizes = get_names(Strtab_bin),
- make_offsets(NamesSizes).
-
-%% @doc Returns the name of the symbol at the given offset. The string table
-%% contains entries of the form {Name, Offset}. If no such offset exists
-%% returns the empty string (`""').
-%% XXX: There might be a bug here because of the "compact" saving the ELF
-%% format uses: e.g. only stores ".rela.text" for ".rela.text" and ".text".
-get_strtab_entry(Strtab, Offset) ->
- case lists:keyfind(Offset, 2, Strtab) of
- {Name, Offset} -> Name;
- false -> ""
- end.
+ parse_strtab(extract_segment_by_name(Elf, ?STRTAB)).
+
+-spec parse_strtab(binary()) -> strtab().
+parse_strtab(StrTabSectionBin) -> StrTabSectionBin.
+
+%% @doc Returns the name of the symbol at the given offset.
+-spec get_strtab_entry(non_neg_integer(), strtab()) -> string().
+get_strtab_entry(Offset, StrTab) ->
+ <<_:Offset/binary, StrBin/binary>> = StrTab,
+ bin_get_string(StrBin).
+
+%% @doc Extracts a null-terminated string from a binary.
+-spec bin_get_string(binary()) -> string().
+%% FIXME: No regard for encoding (just happens to work for ASCII and Latin-1)
+bin_get_string(<<0, _/binary>>) -> [];
+bin_get_string(<<Char, Rest/binary>>) -> [Char|bin_get_string(Rest)].
%%------------------------------------------------------------------------------
%% Functions to manipulate Relocations
%%------------------------------------------------------------------------------
-%% @doc This function gets as argument an ELF binary file and returns a list
-%% with all .rela.rodata labels (i.e. constants and literals in code)
-%% or an empty list if no ".rela.rodata" section exists in code.
--spec get_rodata_relocs(elf()) -> [offset()].
-get_rodata_relocs(Elf) ->
- case is64bit() of
- true ->
- %% Only care about the addends (== offsets):
- get_rela_addends(extract_rela(Elf, ?RODATA));
- false ->
- %% Find offsets hardcoded in ".rodata" entry
- %%XXX: Treat all 0s as padding and skip them!
- [SkipPadding || SkipPadding <- extract_rodata(Elf), SkipPadding =/= 0]
- end.
-
--spec get_rela_addends([elf_rela()]) -> [offset()].
-get_rela_addends(RelaEntries) ->
- [rela_addend(E) || E <- RelaEntries].
-
-%% @doc Extract a list of the form `[{SymbolName, Offset}]' with all relocatable
-%% symbols and their offsets in the code from the ".text" section.
--spec get_text_relocs(elf()) -> [{name(), offset()}].
-get_text_relocs(Elf) ->
- %% Only care about the symbol table index and the offset:
- NameOffsetTemp = [{?ELF_R_SYM(r_info(E)), r_offset(E)}
- || E <- extract_rela(Elf, ?TEXT)],
- {NameIndices, ActualOffsets} = lists:unzip(NameOffsetTemp),
- %% Find the names of the symbols:
- %%
- %% Get those symbol table entries that are related to Text relocs:
- Symtab = extract_symtab(Elf),
- SymtabEs = [get_symtab_entry(Symtab, Index) || Index <- NameIndices],
- %XXX: not zero-indexed!
- %% Symbol table entries contain the offset of the name of the symbol in
- %% String Table:
- SymtabEs2 = [sym_name(E) || E <- SymtabEs], %XXX: Do we need to sort SymtabE?
- %% Get string table entries ([{Name, Offset in strtab section}]). Keep only
- %% relevant entries:
- Strtab = extract_strtab(Elf),
- Relevant = [get_strtab_entry(Strtab, Off) || Off <- SymtabEs2],
- %% Zip back with actual offsets:
- lists:zip(Relevant, ActualOffsets).
-
%% @doc Extract the Relocations segment for section `Name' (that is passed
%% as second argument) from an ELF formated Object file binary.
--spec extract_rela(elf(), name()) -> [elf_rel() | elf_rela()].
+-spec extract_rela(elf(), name()) -> [elf_rel()].
+
+-ifdef(BIT32).
extract_rela(Elf, Name) ->
- SegName =
- case is64bit() of
- true -> ?RELA(Name); % ELF-64 uses ".rela"
- false -> ?REL(Name) % ...while ELF-32 uses ".rel"
- end,
- Rela_bin = extract_segment_by_name(Elf, SegName),
- get_rela_entries(Rela_bin, []).
-
-get_rela_entries(<<>>, Acc) ->
- lists:reverse(Acc);
-get_rela_entries(Bin, Acc) ->
- E = case is64bit() of
- true ->
- <<%% Structural pattern matching on fields of a Rela Entry.
- Offset:?bits(?R_OFFSET_SIZE)/integer-little,
- Info:?bits(?R_INFO_SIZE)/integer-little,
- Addend:?bits(?R_ADDEND_SIZE)/integer-little,
- Rest/binary
- >> = Bin,
- mk_rela(Offset, Info, Addend);
- false ->
- <<%% Structural pattern matching on fields of a Rel Entry.
- Offset:?bits(?R_OFFSET_SIZE)/integer-little,
- Info:?bits(?R_INFO_SIZE)/integer-little,
- Rest/binary
- >> = Bin,
- mk_rel(Offset, Info)
- end,
- get_rela_entries(Rest, [E | Acc]).
-
-%% %% @doc Extract the `EntryNum' (serial number) Relocation Entry.
-%% get_rela_entry(Rela, EntryNum) ->
-%% lists:nth(EntryNum + 1, Rela).
+ SecData = extract_segment_by_name(Elf, Name),
+ [#elf_rel{offset=Offset, symbol=elf_symbol(?ELF_R_SYM(Info), Elf),
+ type=decode_reloc_type(?ELF_R_TYPE(Info)),
+ addend=read_implicit_addend(Offset, SecData)}
+ || <<Offset:?bits(?R_OFFSET_SIZE)/little,
+ Info:?bits(?R_INFO_SIZE)/little % 386 uses ".rel"
+ >> <= extract_segment_by_name(Elf, ?REL(Name))].
+
+%% The only types HiPE knows how to patch
+decode_reloc_type(1) -> '32';
+decode_reloc_type(2) -> 'pc32'.
+
+read_implicit_addend(Offset, Section) ->
+ %% All x86 relocation types uses 'word32' relocation fields; i.e. 32-bit LE.
+ <<_:Offset/binary, Addend:32/signed-little, _/binary>> = Section,
+ Addend.
+
+-else. %% BIT32
+extract_rela(Elf, Name) ->
+ [#elf_rel{offset=Offset, symbol=elf_symbol(?ELF_R_SYM(Info), Elf),
+ type=decode_reloc_type(?ELF_R_TYPE(Info)), addend=Addend}
+ || <<Offset:?bits(?R_OFFSET_SIZE)/little,
+ Info:?bits(?R_INFO_SIZE)/little,
+ Addend:?bits(?R_ADDEND_SIZE)/signed-little % X86_64 uses ".rela"
+ >> <= extract_segment_by_name(Elf, ?RELA(Name))].
+
+decode_reloc_type(1) -> '64';
+decode_reloc_type(2) -> 'pc32';
+decode_reloc_type(10) -> '32'.
+-endif. %% BIT32
%%------------------------------------------------------------------------------
%% Functions to manipulate Executable Code segment
@@ -615,19 +568,6 @@ get_gccexntab_callsites(CSTab, Acc) ->
get_gccexntab_callsites(More, [GccCS | Acc]).
%%------------------------------------------------------------------------------
-%% Functions to manipulate Read-only Data (.rodata)
-%%------------------------------------------------------------------------------
-extract_rodata(Elf) ->
- Rodata_bin = extract_segment_by_name(Elf, ?RODATA),
- get_rodata_entries(Rodata_bin, []).
-
-get_rodata_entries(<<>>, Acc) ->
- lists:reverse(Acc);
-get_rodata_entries(Rodata_bin, Acc) ->
- <<Num:?bits(?ELF_ADDR_SIZE)/integer-little, More/binary>> = Rodata_bin,
- get_rodata_entries(More, [Num | Acc]).
-
-%%------------------------------------------------------------------------------
%% Helper functions
%%------------------------------------------------------------------------------
@@ -647,107 +587,15 @@ get_binary_segment(Bin, Offset, Size) ->
%% There are handy macros defined in elf_format.hrl for all Standard
%% Section Names.
-spec extract_segment_by_name(elf(), string()) -> binary().
-extract_segment_by_name(Elf, SectionName) ->
- %% Extract Section Header Table and Section Header String Table from binary
- SHdrTable = extract_shdrtab(Elf),
- Names = extract_shstrtab(Elf),
- %% Zip to a list of (Name,ShdrE)
- [_Zero | ShdrEs] = lists:keysort(2, SHdrTable), % Skip first entry (zeros).
- L = lists:zip(Names, ShdrEs),
+extract_segment_by_name(#elf{file=ElfBin, sec_nam=SecNam}, SectionName) ->
%% Find Section Header Table entry by name
- case lists:keyfind(SectionName, 1, L) of
- {SectionName, ShdrE} -> %% Note: Same name.
- #elf_shdr{offset = Offset, size = Size} = ShdrE,
- get_binary_segment(Elf, Offset, Size);
- false -> %% Not found.
+ case SecNam of
+ #{SectionName := #elf_shdr{offset=Offset, size=Size}} ->
+ get_binary_segment(ElfBin, Offset, Size);
+ #{} -> %% Not found.
<<>>
end.
-%% @doc Extracts a list of strings with (zero-separated) names from a binary.
-%% Returns tuples of `{Name, Size}'.
-%% XXX: Skip trailing 0.
--spec get_names(<<_:8,_:_*8>>) -> name_sizes().
-get_names(<<0, Bin/binary>>) ->
- NamesSizes = get_names(Bin, []),
- fix_names(NamesSizes, []).
-
-get_names(<<>>, Acc) ->
- lists:reverse(Acc);
-get_names(Bin, Acc) ->
- {Name, MoreNames} = bin_get_string(Bin),
- get_names(MoreNames, [{Name, length(Name)} | Acc]).
-
-%% @doc Fix names:
-%% e.g. If ".rela.text" exists, ".text" does not. Same goes for
-%% ".rel.text". In that way, the Section Header String Table is more
-%% compact. Add ".text" just *before* the corresponding rela-field,
-%% etc.
--spec fix_names(name_sizes(), name_sizes()) -> name_sizes().
-fix_names([], Acc) ->
- lists:reverse(Acc);
-fix_names([{Name, Size}=T | Names], Acc) ->
- case is64bit() of
- true ->
- case string:str(Name, ".rela") =:= 1 of
- true -> %% Name starts with ".rela":
- Section = string:substr(Name, 6),
- fix_names(Names, [{Section, Size - 5}
- | [T | Acc]]); % XXX: Is order ok? (".text"
- % always before ".rela.text")
- false -> %% Name does not start with ".rela":
- fix_names(Names, [T | Acc])
- end;
- false ->
- case string:str(Name, ".rel") =:= 1 of
- true -> %% Name starts with ".rel":
- Section = string:substr(Name, 5),
- fix_names(Names, [{Section, Size - 4}
- | [T | Acc]]); % XXX: Is order ok? (".text"
- % always before ".rela.text")
- false -> %% Name does not start with ".rel":
- fix_names(Names, [T | Acc])
- end
- end.
-
-
-%% @doc A function that byte-reverses a binary. This might be needed because of
-%% little (fucking!) endianess.
--spec bin_reverse(binary()) -> binary().
-bin_reverse(Bin) when is_binary(Bin) ->
- bin_reverse(Bin, <<>>).
-
--spec bin_reverse(binary(), binary()) -> binary().
-bin_reverse(<<>>, Acc) ->
- Acc;
-bin_reverse(<<Head, More/binary>>, Acc) ->
- bin_reverse(More, <<Head, Acc/binary>>).
-
-%% @doc A function that extracts a null-terminated string from a binary. It
-%% returns the found string along with the rest of the binary.
--spec bin_get_string(binary()) -> {string(), binary()}.
-bin_get_string(Bin) ->
- bin_get_string(Bin, <<>>).
-
-bin_get_string(<<>>, BinAcc) ->
- Bin = bin_reverse(BinAcc), % little endian!
- {binary_to_list(Bin), <<>>};
-bin_get_string(<<0, MoreBin/binary>>, BinAcc) ->
- Bin = bin_reverse(BinAcc), % little endian!
- {binary_to_list(Bin), MoreBin};
-bin_get_string(<<Letter, Tail/binary>>, BinAcc) ->
- bin_get_string(Tail, <<Letter, BinAcc/binary>>).
-
-%% @doc
-make_offsets(NamesSizes) ->
- {Names, Sizes} = lists:unzip(NamesSizes),
- Offsets = make_offsets_from_sizes(Sizes, 1, []),
- lists:zip(Names, Offsets).
-
-make_offsets_from_sizes([], _, Acc) ->
- lists:reverse(Acc);
-make_offsets_from_sizes([Size | Sizes], Cur, Acc) ->
- make_offsets_from_sizes(Sizes, Size+Cur+1, [Cur | Acc]). % For the "."!
-
%% @doc Little-Endian Base 128 (LEB128) Decoder
%% This function extracts the <b>first</b> LEB128-encoded integer in a
%% binary and returns that integer along with the remaining binary. This is
@@ -770,21 +618,3 @@ leb128_decode(LebNum, NoOfBits, Acc) ->
<<Num:Size/integer>> = <<NextBundle:7/bits, Acc/bits>>,
{Num, MoreLebNums}
end.
-
-%% @doc Extract ELF Class from ELF header and export symbol to process
-%% dictionary.
--spec set_architecture_flag(elf()) -> 'ok'.
-set_architecture_flag(Elf) ->
- %% Extract information about ELF Class from ELF Header
- <<16#7f, $E, $L, $F, EI_Class, _MoreHeader/binary>>
- = get_binary_segment(Elf, 0, ?ELF_EHDR_SIZE),
- put(elf_class, EI_Class),
- ok.
-
-%% @doc Read from object file header if the file class is ELF32 or ELF64.
--spec is64bit() -> boolean().
-is64bit() ->
- case get(elf_class) of
- ?ELFCLASS64 -> true;
- ?ELFCLASS32 -> false
- end.
diff --git a/lib/hipe/llvm/elf_format.hrl b/lib/hipe/llvm/elf_format.hrl
index 7a3cdfead6..57a36f0c3e 100644
--- a/lib/hipe/llvm/elf_format.hrl
+++ b/lib/hipe/llvm/elf_format.hrl
@@ -486,3 +486,43 @@
%% Misc.
%%------------------------------------------------------------------------------
-define(bits(Bytes), ((Bytes) bsl 3)).
+
+%%------------------------------------------------------------------------------
+%% Exported record and type declarations for 'elf_format' module
+%%------------------------------------------------------------------------------
+
+%% Section header entries
+-record(elf_shdr,
+ {name :: elf_format:name() % Section name
+ ,type :: elf_format:shdr_type() % Section type
+ ,flags :: elf_format:bitflags() % Section attributes
+ ,addr :: elf_format:offset() % Virtual address in memory
+ ,offset :: elf_format:offset() % Offset in file
+ ,size :: elf_format:size() % Size of section
+ ,link :: non_neg_integer() % Link to other section
+ ,info :: non_neg_integer() % Miscellaneous information
+ ,addralign :: elf_format:size() % Address align boundary
+ ,entsize :: elf_format:size() % Size of entries, if section has
+ % table
+ }).
+-type elf_shdr() :: #elf_shdr{}.
+
+%% Symbol table entries
+-record(elf_sym,
+ {name :: elf_format:name() % Symbol name
+ ,bind :: elf_format:sym_bind() % Symbol binding
+ ,type :: elf_format:sym_type() % Symbol type
+ ,value :: elf_format:valueoff() % Symbol value
+ ,size :: elf_format:size() % Size of object
+ ,section :: undefined | abs | elf_shdr()
+ }).
+-type elf_sym() :: #elf_sym{}.
+
+%% Relocations
+-record(elf_rel,
+ {offset :: elf_format:offset()
+ ,type :: elf_format:reloc_type()
+ ,addend :: elf_format:addend()
+ ,symbol :: elf_sym()
+ }).
+-type elf_rel() :: #elf_rel{}.
diff --git a/lib/hipe/llvm/hipe_llvm.erl b/lib/hipe/llvm/hipe_llvm.erl
index 5e33731a2b..c2547dd89e 100644
--- a/lib/hipe/llvm/hipe_llvm.erl
+++ b/lib/hipe/llvm/hipe_llvm.erl
@@ -234,7 +234,7 @@
function_arg_type_list/1
]).
--export([pp_ins_list/2, pp_ins/2]).
+-export([pp_ins_list/3, pp_ins/3]).
%%-----------------------------------------------------------------------------
@@ -765,13 +765,17 @@ function_arg_type_list(#llvm_fun{arg_type_list=Arg_type_list}) ->
%% Pretty-printer Functions
%%----------------------------------------------------------------------------
-%% @doc Pretty-print a list of LLVM instructions to a Device.
-pp_ins_list(_Dev, []) -> ok;
-pp_ins_list(Dev, [I|Is]) ->
- pp_ins(Dev, I),
- pp_ins_list(Dev, Is).
+-type llvm_version() :: {Major :: integer(), Minor :: integer()}.
-pp_ins(Dev, I) ->
+%% @doc Pretty-print a list of LLVM instructions to a Device, using syntax
+%% compatible with LLVM v. Major.Minor
+-spec pp_ins_list(file:io_device(), llvm_version(), [llvm_instr()]) -> ok.
+pp_ins_list(_Dev, _Ver, []) -> ok;
+pp_ins_list(Dev, Ver={_,_}, [I|Is]) ->
+ pp_ins(Dev, Ver, I),
+ pp_ins_list(Dev, Ver, Is).
+
+pp_ins(Dev, Ver, I) ->
case indent(I) of
true -> write(Dev, " ");
false -> ok
@@ -861,7 +865,7 @@ pp_ins(Dev, I) ->
true -> write(Dev, "volatile ");
false -> ok
end,
- pp_type(Dev, load_p_type(I)),
+ pp_dereference_type(Dev, Ver, load_p_type(I)),
write(Dev, [" ", load_pointer(I), " "]),
case load_alignment(I) of
[] -> ok;
@@ -897,7 +901,7 @@ pp_ins(Dev, I) ->
true -> write(Dev, "inbounds ");
false -> ok
end,
- pp_type(Dev, getelementptr_p_type(I)),
+ pp_dereference_type(Dev, Ver, getelementptr_p_type(I)),
write(Dev, [" ", getelementptr_value(I)]),
pp_typed_idxs(Dev, getelementptr_typed_idxs(I)),
write(Dev, "\n");
@@ -958,12 +962,16 @@ pp_ins(Dev, I) ->
pp_args(Dev, fun_def_arglist(I)),
write(Dev, ") "),
pp_options(Dev, fun_def_fn_attrs(I)),
+ case Ver >= {3,7} of false -> ok; true ->
+ write(Dev, "personality i32 (i32, i64, i8*,i8*)* "
+ "@__gcc_personality_v0 ")
+ end,
case fun_def_align(I) of
[] -> ok;
N -> write(Dev, ["align ", N])
end,
write(Dev, "{\n"),
- pp_ins_list(Dev, fun_def_body(I)),
+ pp_ins_list(Dev, Ver, fun_def_body(I)),
write(Dev, "}\n");
#llvm_fun_decl{} ->
write(Dev, "declare "),
@@ -992,8 +1000,12 @@ pp_ins(Dev, I) ->
pp_type(Dev, const_decl_type(I)),
write(Dev, [" ", const_decl_value(I), "\n"]);
#llvm_landingpad{} ->
- write(Dev, "landingpad { i8*, i32 } personality i32 (i32, i64, i8*,i8*)*
- @__gcc_personality_v0 cleanup\n");
+ write(Dev, "landingpad { i8*, i32 } "),
+ case Ver < {3,7} of false -> ok; true ->
+ write(Dev, "personality i32 (i32, i64, i8*,i8*)* "
+ "@__gcc_personality_v0 ")
+ end,
+ write(Dev, "cleanup\n");
#llvm_asm{} ->
write(Dev, [asm_instruction(I), "\n"]);
#llvm_adj_stack{} ->
@@ -1002,13 +1014,27 @@ pp_ins(Dev, I) ->
pp_type(Dev, adj_stack_type(I)),
write(Dev, [" ", adj_stack_offset(I),")\n"]);
#llvm_branch_meta{} ->
- write(Dev, ["!", branch_meta_id(I), " = metadata !{metadata !\"branch_weights\",
- i32 ", branch_meta_true_weight(I), ", i32 ",
- branch_meta_false_weight(I), "}\n"]);
+ write(Dev, ["!", branch_meta_id(I), " = "]),
+ if Ver < {3,6} -> write(Dev, "metadata !{metadata ");
+ Ver >= {3,6} -> write(Dev, "!{ ")
+ end,
+ write(Dev, ["!\"branch_weights\", i32 ", branch_meta_true_weight(I),
+ ", i32 ", branch_meta_false_weight(I), "}\n"]);
Other ->
exit({?MODULE, pp_ins, {"Unknown LLVM instruction", Other}})
end.
+%% @doc Print the type of a dereference in an LLVM instruction using syntax
+%% parsable by the specified LLVM version.
+pp_dereference_type(Dev, Ver, Type) ->
+ case Ver >= {3,7} of
+ false -> ok;
+ true ->
+ pp_type(Dev, pointer_type(Type)),
+ write(Dev, ", ")
+ end,
+ pp_type(Dev, Type).
+
%% @doc Pretty-print a list of types
pp_type_list(_Dev, []) -> ok;
pp_type_list(Dev, [T]) ->
diff --git a/lib/hipe/llvm/hipe_llvm_main.erl b/lib/hipe/llvm/hipe_llvm_main.erl
index 3c24425828..476d6fb49c 100644
--- a/lib/hipe/llvm/hipe_llvm_main.erl
+++ b/lib/hipe/llvm/hipe_llvm_main.erl
@@ -13,7 +13,7 @@
%% chain is invoked in order to produce an object file.
rtl_to_native(MFA, RTL, Roots, Options) ->
%% Compile to LLVM and get Instruction List (along with infos)
- {LLVMCode, RelocsDict, ConstTab} =
+ {LLVMCode, RelocsDict0, ConstTab0} =
hipe_rtl_to_llvm:translate(RTL, Roots),
%% Fix function name to an acceptable LLVM identifier (needed for closures)
{_Module, Fun, Arity} = hipe_rtl_to_llvm:fix_mfa_name(MFA),
@@ -24,34 +24,33 @@ rtl_to_native(MFA, RTL, Roots, Options) ->
%% Extract information from object file
%%
ObjBin = open_object_file(ObjectFile),
- %% Read and set the ELF class
- elf_format:set_architecture_flag(ObjBin),
+ Obj = elf_format:read(ObjBin),
%% Get labels info (for switches and jump tables)
- Labels = elf_format:get_rodata_relocs(ObjBin),
- {Switches, Closures} = get_tables(ObjBin),
+ Labels = elf_format:extract_rela(Obj, ?RODATA),
+ Tables = get_tables(Obj),
%% Associate Labels with Switches and Closures with stack args
- {SwitchInfos, ExposedClosures} =
- correlate_labels(Switches ++ Closures, Labels),
+ {SwitchInfos, ExposedClosures} = correlate_labels(Tables, Labels),
%% SwitchInfos: [{"table_50", [Labels]}]
%% ExposedClosures: [{"table_closures", [Labels]}]
-
+
%% Labelmap contains the offsets of the labels in the code that are
%% used for switch's jump tables
- LabelMap = create_labelmap(MFA, SwitchInfos, RelocsDict),
+ LabelMap = create_labelmap(MFA, SwitchInfos, RelocsDict0),
+ {RelocsDict, ConstTab} = extract_constants(RelocsDict0, ConstTab0, Obj),
%% Get relocation info
- TextRelocs = elf_format:get_text_relocs(ObjBin),
+ TextRelocs = elf_format:extract_rela(Obj, ?TEXT),
%% AccRefs contains the offsets of all references to relocatable symbols in
%% the code:
AccRefs = fix_relocations(TextRelocs, RelocsDict, MFA),
%% Get stack descriptors
- SDescs = get_sdescs(ObjBin),
+ SDescs = get_sdescs(Obj),
%% FixedSDescs are the stack descriptors after correcting calls that have
%% arguments in the stack
FixedSDescs =
fix_stack_descriptors(RelocsDict, AccRefs, SDescs, ExposedClosures),
Refs = AccRefs ++ FixedSDescs,
%% Get binary code from object file
- BinCode = elf_format:extract_text(ObjBin),
+ BinCode = elf_format:extract_text(Obj),
%% Remove temp files (if needed)
ok = remove_temp_folder(Dir, Options),
%% Return the code together with information that will be used in the
@@ -78,7 +77,8 @@ compile_with_llvm(FunName, Arity, LLVMCode, Options, UseBuffer) ->
false -> []
end,
{ok, File_llvm} = file:open(Dir ++ Filename ++ ".ll", OpenOpts),
- hipe_llvm:pp_ins_list(File_llvm, LLVMCode),
+ Ver = hipe:get_llvm_version(), %% Should probably cache this
+ hipe_llvm:pp_ins_list(File_llvm, Ver, LLVMCode),
%% delayed_write can cause file:close not to do a close, hence the two calls
ok = file:close(File_llvm),
__ = file:close(File_llvm),
@@ -158,12 +158,10 @@ trans_optlev_flag(Tool, Options) ->
%%------------------------------------------------------------------------------
%% @doc Get switch table and closure table.
+-spec get_tables(elf_format:elf()) -> [elf_sym()].
get_tables(Elf) ->
- %% Search Symbol Table for an entry with name prefixed with "table_":
- Triples = elf_format:get_tab_entries(Elf),
- Switches = [T || T={"table_" ++ _, _, _} <- Triples],
- Closures = [T || T={"table_closures" ++ _, _, _} <- Switches],
- {Switches, Closures}.
+ %% Search Symbol Table for entries where name is prefixed with "table_":
+ [S || S=#elf_sym{name="table_" ++ _} <- elf_format:elf_symbols(Elf)].
%% @doc This function associates symbols who point to some table of labels with
%% the corresponding offsets of the labels in the code. These tables can
@@ -171,14 +169,12 @@ get_tables(Elf) ->
%% of blocks that contain closure calls with more than ?NR_ARG_REGS.
correlate_labels([], _L) -> {[], []};
correlate_labels(Tables, Labels) ->
- %% Sort "Tables" based on "ValueOffsets"
- OffsetSortedTb = lists:ukeysort(2, Tables),
- %% Unzip offset-sorted list of "Switches"
- {Names, _Offsets, TablesSizeList} = lists:unzip3(OffsetSortedTb),
- %% Associate switch names with labels
- L = split_list(Labels, TablesSizeList),
- %% Zip back! (to [{SwitchName, Values}])
- NamesValues = lists:zip(Names, L),
+ %% Assumes that the relocations are sorted
+ RelocTree = gb_trees:from_orddict(
+ [{Rel#elf_rel.offset, Rel#elf_rel.addend} || Rel <- Labels]),
+ %% Lookup all relocations pertaining to each symbol
+ NamesValues = [{Name, lookup_range(Value, Value+Size, RelocTree)}
+ || #elf_sym{name=Name, value=Value, size=Size} <- Tables],
case lists:keytake("table_closures", 1, NamesValues) of
false -> %% No closures in the code, no closure table
{NamesValues, []};
@@ -186,6 +182,17 @@ correlate_labels(Tables, Labels) ->
{SwitchesNV, ClosureTableNV}
end.
+%% Fetches all values with a key in [Low, Hi)
+-spec lookup_range(_::K, _::K, gb_trees:tree(K,V)) -> [_::V].
+lookup_range(Low, Hi, Tree) ->
+ lookup_range_1(Hi, gb_trees:iterator_from(Low, Tree)).
+
+lookup_range_1(Hi, Iter0) ->
+ case gb_trees:next(Iter0) of
+ {Key, Value, Iter} when Key < Hi -> [Value | lookup_range_1(Hi, Iter)];
+ _ -> []
+ end.
+
%% @doc Create a gb_tree which contains information about the labels that used
%% for switch's jump tables. The keys of the gb_tree are of the form
%% {MFA, Label} and the values are the actual Offsets.
@@ -213,40 +220,80 @@ insert_to_labelmap([{Key, Value}|Rest], LabelMap) ->
insert_to_labelmap(Rest, LabelMap)
end.
+%% Find any LLVM-generated constants and add them to the constant table
+extract_constants(RelocsDict0, ConstTab0, Obj) ->
+ TextRelocs = elf_format:extract_rela(Obj, ?TEXT),
+ AnonConstSections =
+ lists:usort([{Sec, Offset}
+ || #elf_rel{symbol=#elf_sym{type=section, section=Sec},
+ addend=Offset} <- TextRelocs]),
+ lists:foldl(
+ fun({#elf_shdr{name=Name, type=progbits, addralign=Align, entsize=EntSize,
+ size=Size} = Section, Offset}, {RelocsDict1, ConstTab1})
+ when EntSize > 0, 0 =:= Size rem EntSize, 0 =:= Offset rem EntSize ->
+ SectionBin = elf_format:section_contents(Section, Obj),
+ Constant = binary:part(SectionBin, Offset, EntSize),
+ {ConstTab, ConstLbl} =
+ hipe_consttab:insert_binary_const(ConstTab1, Align, Constant),
+ {dict:store({anon, Name, Offset}, {constant, ConstLbl}, RelocsDict1),
+ ConstTab}
+ end, {RelocsDict0, ConstTab0}, AnonConstSections).
+
%% @doc Correlate object file relocation symbols with info from translation to
%% llvm code.
fix_relocations(Relocs, RelocsDict, MFA) ->
- fix_relocs(Relocs, RelocsDict, MFA, []).
-
-fix_relocs([], _, _, RelocAcc) -> RelocAcc;
-fix_relocs([{Name, Offset}|Rs], RelocsDict, {ModName,_,_}=MFA, RelocAcc) ->
+ lists:map(fun(Reloc) -> fix_reloc(Reloc, RelocsDict, MFA) end, Relocs).
+
+%% Relocation types and expected addends for x86 and amd64
+-define(PCREL_T, 'pc32').
+-define(PCREL_A, -4). %% Hard-coded in hipe_x86.c and hipe_amd64.c
+-ifdef(BIT32).
+-define(ABS_T, '32').
+-define(ABS_A, _). %% We support any addend
+-else.
+-define(ABS_T, '64').
+-define(ABS_A, 0).
+-endif.
+
+fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=undefined, type=notype},
+ offset=Offset, type=?PCREL_T, addend=?PCREL_A},
+ RelocsDict, {ModName,_,_}) when Name =/= "" ->
case dict:fetch(Name, RelocsDict) of
- {atom, AtomName} ->
- fix_relocs(Rs, RelocsDict, MFA,
- [{?LOAD_ATOM, Offset, AtomName}|RelocAcc]);
- {constant, Label} ->
- fix_relocs(Rs, RelocsDict, MFA,
- [{?LOAD_ADDRESS, Offset, {constant, Label}}|RelocAcc]);
- {switch, _, JTabLab} -> %% Treat switch exactly as constant
- fix_relocs(Rs, RelocsDict, MFA,
- [{?LOAD_ADDRESS, Offset, {constant, JTabLab}}|RelocAcc]);
- {closure, _}=Closure ->
- fix_relocs(Rs, RelocsDict, MFA,
- [{?LOAD_ADDRESS, Offset, Closure}|RelocAcc]);
- {call, {bif, BifName, _}} ->
- fix_relocs(Rs, RelocsDict, MFA,
- [{?CALL_LOCAL, Offset, BifName}|RelocAcc]);
+ {call, {bif, BifName, _}} -> {?CALL_LOCAL, Offset, BifName};
%% MFA calls to functions in the same module are of type 3, while all
%% other MFA calls are of type 2.
- {call, {ModName,_F,_A}=CallMFA} ->
- fix_relocs(Rs, RelocsDict, MFA,
- [{?CALL_LOCAL, Offset, CallMFA}|RelocAcc]);
- {call, CallMFA} ->
- fix_relocs(Rs, RelocsDict, MFA,
- [{?CALL_REMOTE, Offset, CallMFA}|RelocAcc]);
- Other ->
- exit({?MODULE, fix_relocs,
- {"Relocation not in relocation dictionary", Other}})
+ %% XXX: Does this code break hot code loading (by transforming external
+ %% calls into local calls?)
+ {call, {ModName,_F,_A}=CallMFA} -> {?CALL_LOCAL, Offset, CallMFA};
+ {call, CallMFA} -> {?CALL_REMOTE, Offset, CallMFA}
+ end;
+fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=undefined, type=notype},
+ offset=Offset, type=?ABS_T, addend=?ABS_A},
+ RelocsDict, _) when Name =/= "" ->
+ case dict:fetch(Name, RelocsDict) of
+ {atom, AtomName} -> {?LOAD_ATOM, Offset, AtomName};
+ {constant, Label} -> {?LOAD_ADDRESS, Offset, {constant, Label}};
+ {closure, _}=Closure -> {?LOAD_ADDRESS, Offset, Closure}
+ end;
+fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=#elf_shdr{name=?TEXT},
+ type=func},
+ offset=Offset, type=?PCREL_T, addend=?PCREL_A},
+ RelocsDict, MFA) when Name =/= "" ->
+ case dict:fetch(Name, RelocsDict) of
+ {call, MFA} -> {?CALL_LOCAL, Offset, MFA}
+ end;
+fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=#elf_shdr{name=?RODATA},
+ type=object},
+ offset=Offset, type=?ABS_T, addend=?ABS_A},
+ RelocsDict, _) when Name =/= "" ->
+ case dict:fetch(Name, RelocsDict) of
+ {switch, _, JTabLab} -> %% Treat switch exactly as constant
+ {?LOAD_ADDRESS, Offset, {constant, JTabLab}}
+ end;
+fix_reloc(#elf_rel{symbol=#elf_sym{type=section, section=#elf_shdr{name=Name}},
+ offset=Offset, type=?ABS_T, addend=Addend}, RelocsDict, _) ->
+ case dict:fetch({anon, Name, Addend}, RelocsDict) of
+ {constant, Label} -> {?LOAD_ADDRESS, Offset, {constant, Label}}
end.
%%------------------------------------------------------------------------------
@@ -271,20 +318,14 @@ get_sdescs(Elf) ->
T = SPCount * ?SP_ADDR_SIZE,
%% Pattern match fields of ".note.gc":
<<SPCount:(?bits(?SP_COUNT_SIZE))/integer-little, % Sanity check!
- SPAddrs:T/binary, % NOTE: In 64bit they are relocs!
+ _SPAddrs:T/binary, % NOTE: In 64bit they are relocs!
StkFrameSize:(?bits(?SP_STKFRAME_SIZE))/integer-little,
StkArity:(?bits(?SP_STKARITY_SIZE))/integer-little,
_LiveRootCount:(?bits(?SP_LIVEROOTCNT_SIZE))/integer-little, % Skip
Roots/binary>> = NoteGC_bin,
LiveRoots = get_liveroots(Roots, []),
- %% Extract information about the safe point addresses:
- SPOffs =
- case elf_format:is64bit() of
- true -> %% Find offsets in ".rela.note.gc":
- elf_format:get_rela_addends(RelaNoteGC);
- false -> %% Find offsets in SPAddrs (in ".note.gc"):
- get_spoffs(SPAddrs, [])
- end,
+ %% Extract the safe point offsets:
+ SPOffs = [A || #elf_rel{addend=A} <- RelaNoteGC],
%% Extract Exception Handler labels:
ExnHandlers = elf_format:get_exn_handlers(Elf),
%% Combine ExnHandlers and Safe point addresses (return addresses):
@@ -300,13 +341,6 @@ get_liveroots(<<Root:?bits(?LR_STKINDEX_SIZE)/integer-little,
MoreRoots/binary>>, Acc) ->
get_liveroots(MoreRoots, [Root | Acc]).
-%% @doc Extracts a bunch of integers (safepoint offsets) from a binary. Returns
-%% a tuple as need for stack descriptors.
-get_spoffs(<<>>, Acc) ->
- lists:reverse(Acc);
-get_spoffs(<<SPOff:?bits(?SP_ADDR_SIZE)/integer-little, More/binary>>, Acc) ->
- get_spoffs(More, [SPOff | Acc]).
-
combine_ras_and_exns(_, [], Acc) ->
lists:reverse(Acc);
combine_ras_and_exns(ExnHandlers, [RA | MoreRAs], Acc) ->
@@ -489,18 +523,3 @@ unique_folder(FunName, Arity, Options) ->
dir_exists(Filename) ->
{Flag, Info} = file:read_file_info(Filename),
(Flag =:= ok) andalso (element(3, Info) =:= directory).
-
-%% @doc Function that takes as arguments a list of integers and a list with
-%% numbers indicating how many items should each tuple have and splits
-%% the original list to a list of lists of integers (with the specified
-%% number of elements), i.e. [ [...], [...] ].
--spec split_list([integer()], [integer()]) -> [ [integer()] ].
-split_list(List, ElemsPerTuple) ->
- split_list(List, ElemsPerTuple, []).
-
--spec split_list([integer()], [integer()], [ [integer()] ]) -> [ [integer()] ].
-split_list([], [], Acc) ->
- lists:reverse(Acc);
-split_list(List, [NumOfElems | MoreNums], Acc) ->
- {L1, L2} = lists:split(NumOfElems, List),
- split_list(L2, MoreNums, [ L1 | Acc]).
diff --git a/lib/hipe/llvm/hipe_rtl_to_llvm.erl b/lib/hipe/llvm/hipe_rtl_to_llvm.erl
index d7d8d1b049..b23d756d6c 100644
--- a/lib/hipe/llvm/hipe_rtl_to_llvm.erl
+++ b/lib/hipe/llvm/hipe_rtl_to_llvm.erl
@@ -266,17 +266,18 @@ trans_alub_overflow(I, Sign, Relocs) ->
T2 = mk_temp(),
%% T1{1}: Boolean variable indicating overflow
I6 = hipe_llvm:mk_extractvalue(T2, ReturnType, T1, "1", []),
- case hipe_rtl:alub_cond(I) of
- Op when Op =:= overflow orelse Op =:= ltu ->
- True_label = mk_jump_label(hipe_rtl:alub_true_label(I)),
- False_label = mk_jump_label(hipe_rtl:alub_false_label(I)),
- MetaData = branch_metadata(hipe_rtl:alub_pred(I));
- not_overflow ->
- True_label = mk_jump_label(hipe_rtl:alub_false_label(I)),
- False_label = mk_jump_label(hipe_rtl:alub_true_label(I)),
- MetaData = branch_metadata(1 - hipe_rtl:alub_pred(I))
- end,
- I7 = hipe_llvm:mk_br_cond(T2, True_label, False_label, MetaData),
+ {TrueLabel, FalseLabel, MetaData} =
+ case hipe_rtl:alub_cond(I) of
+ Op when Op =:= overflow orelse Op =:= ltu ->
+ {mk_jump_label(hipe_rtl:alub_true_label(I)),
+ mk_jump_label(hipe_rtl:alub_false_label(I)),
+ branch_metadata(hipe_rtl:alub_pred(I))};
+ not_overflow ->
+ {mk_jump_label(hipe_rtl:alub_false_label(I)),
+ mk_jump_label(hipe_rtl:alub_true_label(I)),
+ branch_metadata(1 - hipe_rtl:alub_pred(I))}
+ end,
+ I7 = hipe_llvm:mk_br_cond(T2, TrueLabel, FalseLabel, MetaData),
{[I7, I6, I5, I4, I3, I2, I1], NewRelocs}.
trans_alub_op(I, Sign) ->
diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src
index aa86b6dc5b..f8487151d7 100644
--- a/lib/hipe/main/hipe.app.src
+++ b/lib/hipe/main/hipe.app.src
@@ -88,6 +88,7 @@
hipe_icode2rtl,
hipe_icode_bincomp,
hipe_icode_callgraph,
+ hipe_icode_call_elim,
hipe_icode_cfg,
hipe_icode_coordinator,
hipe_icode_ebb,
diff --git a/lib/hipe/main/hipe.erl b/lib/hipe/main/hipe.erl
index 0e32da1d36..981265b3e9 100644
--- a/lib/hipe/main/hipe.erl
+++ b/lib/hipe/main/hipe.erl
@@ -200,8 +200,9 @@
compile/4,
compile_core/4,
file/1,
- file/2,
- llvm_support_available/0,
+ file/2,
+ get_llvm_version/0,
+ llvm_support_available/0,
load/1,
help/0,
help_hiper/0,
@@ -1165,6 +1166,9 @@ option_text(caller_save_spill_restore) ->
"Activates caller save register spills and restores";
option_text(debug) ->
"Outputs internal debugging information during compilation";
+option_text(icode_call_elim) ->
+ "Performs call elimination of BIFs that are side-effect free\n" ++
+ "only on some argument types";
option_text(icode_range) ->
"Performs integer range analysis on the Icode level";
option_text(icode_ssa_check) ->
@@ -1318,6 +1322,7 @@ opt_keys() ->
get_called_modules,
split_arith,
split_arith_unsafe,
+ icode_call_elim,
icode_inline_bifs,
icode_ssa_check,
icode_ssa_copy_prop,
@@ -1399,7 +1404,7 @@ o1_opts(TargetArch) ->
o2_opts(TargetArch) ->
Common = [icode_ssa_const_prop, icode_ssa_copy_prop, % icode_ssa_struct_reuse,
- icode_type, icode_inline_bifs, rtl_lcm,
+ icode_type, icode_inline_bifs, icode_call_elim, rtl_lcm,
rtl_ssa, rtl_ssa_const_prop,
spillmin_color, use_indexing, remove_comments,
concurrent_comp, binary_opt | o1_opts(TargetArch)],
@@ -1429,6 +1434,7 @@ opt_negations() ->
{no_icode_inline_bifs, icode_inline_bifs},
{no_icode_range, icode_range},
{no_icode_split_arith, icode_split_arith},
+ {no_icode_call_elim, icode_call_elim},
{no_icode_ssa_check, icode_ssa_check},
{no_icode_ssa_copy_prop, icode_ssa_copy_prop},
{no_icode_ssa_const_prop, icode_ssa_const_prop},
@@ -1479,18 +1485,25 @@ opt_expansions(TargetArch) ->
[{o1, o1_opts(TargetArch)},
{o2, o2_opts(TargetArch)},
{o3, o3_opts(TargetArch)},
- {to_llvm, llvm_opts(o3)},
- {{to_llvm, o0}, llvm_opts(o0)},
- {{to_llvm, o1}, llvm_opts(o1)},
- {{to_llvm, o2}, llvm_opts(o2)},
- {{to_llvm, o3}, llvm_opts(o3)},
+ {to_llvm, llvm_opts(o3, TargetArch)},
+ {{to_llvm, o0}, llvm_opts(o0, TargetArch)},
+ {{to_llvm, o1}, llvm_opts(o1, TargetArch)},
+ {{to_llvm, o2}, llvm_opts(o2, TargetArch)},
+ {{to_llvm, o3}, llvm_opts(o3, TargetArch)},
{x87, [x87, inline_fp]},
{inline_fp, case TargetArch of %% XXX: Temporary until x86 has sse2
x86 -> [x87, inline_fp];
_ -> [inline_fp] end}].
-llvm_opts(O) ->
- [to_llvm, {llvm_opt, O}, {llvm_llc, O}].
+llvm_opts(O, TargetArch) ->
+ Base = [to_llvm, {llvm_opt, O}, {llvm_llc, O}],
+ case TargetArch of
+ %% A llvm bug present in 3.4 through (at least) 3.8 miscompiles x86
+ %% functions that have floats are spilled to stack by clobbering the process
+ %% pointer (ebp) trying to realign the stack pointer.
+ x86 -> [no_inline_fp | Base];
+ _ -> Base
+ end.
%% This expands "basic" options, which may be tested early and cannot be
%% in conflict with options found in the source code.
@@ -1520,7 +1533,8 @@ expand_options(Opts, TargetArch) ->
proplists:normalize(Opts, [{negations, opt_negations()},
{aliases, opt_aliases()},
{expand, opt_basic_expansions()},
- {expand, opt_expansions(TargetArch)}]).
+ {expand, opt_expansions(TargetArch)},
+ {negations, opt_negations()}]).
-spec check_options(comp_options()) -> 'ok'.
@@ -1538,18 +1552,27 @@ check_options(Opts) ->
-spec llvm_support_available() -> boolean().
llvm_support_available() ->
- get_llvm_version() >= 3.4.
+ get_llvm_version() >= {3,4}.
+
+-type llvm_version() :: {Major :: integer(), Minor :: integer()}.
+-spec get_llvm_version() -> llvm_version() | {0, 0}.
get_llvm_version() ->
OptStr = os:cmd("opt -version"),
SubStr = "LLVM version ", N = length(SubStr),
case string:str(OptStr, SubStr) of
0 -> % No opt available
- 0.0;
+ {0, 0};
S ->
- case string:to_float(string:sub_string(OptStr, S + N)) of
- {error, _} -> 0.0; %XXX: Assumes no revision numbers in versioning
- {Float, _} -> Float
+ case string:tokens(string:sub_string(OptStr, S + N), ".") of
+ [MajorS, MinorS | _] ->
+ case {string:to_integer(MajorS), string:to_integer(MinorS)} of
+ {{Major, ""}, {Minor, _}}
+ when is_integer(Major), is_integer(Minor) ->
+ {Major, Minor};
+ _ -> {0, 0}
+ end;
+ _ -> {0, 0} %XXX: Assumes no revision numbers in versioning
end
end.
diff --git a/lib/hipe/main/hipe_main.erl b/lib/hipe/main/hipe_main.erl
index be5050e155..b9d783d20a 100644
--- a/lib/hipe/main/hipe_main.erl
+++ b/lib/hipe/main/hipe_main.erl
@@ -284,8 +284,9 @@ icode_ssa_type(IcodeSSA, MFA, Options, Servers) ->
false -> AnnIcode1
end,
AnnIcode3 = icode_range_analysis(AnnIcode2, MFA, Options, Servers),
- pp(AnnIcode3, MFA, icode, pp_range_icode, Options, Servers),
- hipe_icode_type:unannotate_cfg(AnnIcode3)
+ AnnIcode4 = icode_eliminate_safe_calls(AnnIcode3, Options),
+ pp(AnnIcode4, MFA, icode, pp_range_icode, Options, Servers),
+ hipe_icode_type:unannotate_cfg(AnnIcode4)
end.
icode_ssa_convert(IcodeCfg, Options) ->
@@ -334,6 +335,15 @@ icode_range_analysis(IcodeSSA, MFA, Options, Servers) ->
IcodeSSA
end.
+icode_eliminate_safe_calls(IcodeSSA, Options) ->
+ case proplists:get_bool(icode_call_elim, Options) of
+ true ->
+ ?option_time(hipe_icode_call_elim:cfg(IcodeSSA),
+ "Icode SSA safe call elimination", Options);
+ false ->
+ IcodeSSA
+ end.
+
icode_ssa_dead_code_elimination(IcodeSSA, Options) ->
IcodeSSA1 = ?option_time(hipe_icode_ssa:remove_dead_code(IcodeSSA),
"Icode SSA dead code elimination pass 2",
diff --git a/lib/hipe/misc/hipe_consttab.erl b/lib/hipe/misc/hipe_consttab.erl
index f361edc79c..226b20fa46 100644
--- a/lib/hipe/misc/hipe_consttab.erl
+++ b/lib/hipe/misc/hipe_consttab.erl
@@ -87,7 +87,8 @@
% {NewTab, Lbl}
insert_sorted_block/4,
insert_block/3,
- %% insert_global_word/2,
+ insert_binary_const/3,
+ %% insert_global_word/2,
%% insert_global_block/4,
%% update_word/3, % update_word(ConstTab, Value) -> {NewTab, Lbl}
%% update_block/5,
@@ -196,6 +197,16 @@ insert_block({ConstTab, RefToLabels, NextLabel}, ElementType, InitList) ->
{ElementType,InitList}),
{insert_backrefs(NewTa, Id, ReferredLabels), Id}.
+%% @doc Inserts a binary constant literal into the const table.
+-spec insert_binary_const(hipe_consttab(), ct_alignment(), binary()) ->
+ {hipe_consttab(), hipe_constlbl()}.
+insert_binary_const(ConstTab, Alignment, Binary)
+ when (Alignment =:= 4 orelse Alignment =:= 8 orelse Alignment =:= 16
+ orelse Alignment =:= 32), is_binary(Binary),
+ size(Binary) rem Alignment =:= 0 ->
+ insert_const(ConstTab, block, Alignment, false,
+ {byte, binary_to_list(Binary)}).
+
%% @spec (ConstTab::hipe_consttab(), ElementType::element_type(),
%% InitList::block(), SortOrder) -> {hipe_consttab(), hipe_constlbl()}
diff --git a/lib/hipe/misc/hipe_consttab.hrl b/lib/hipe/misc/hipe_consttab.hrl
index d2dbbe509c..550da0455c 100644
--- a/lib/hipe/misc/hipe_consttab.hrl
+++ b/lib/hipe/misc/hipe_consttab.hrl
@@ -20,7 +20,7 @@
%%
%%-----------------------------------------------------------------------------
--type ct_alignment() :: 4 | 8.
+-type ct_alignment() :: 4 | 8 | 16 | 32.
-type hipe_constlbl() :: non_neg_integer().
-type hipe_consttab() :: {dict:dict(), [hipe_constlbl()], hipe_constlbl()}.
diff --git a/lib/hipe/test/Makefile b/lib/hipe/test/Makefile
index 09f4fd2129..544888719f 100644
--- a/lib/hipe/test/Makefile
+++ b/lib/hipe/test/Makefile
@@ -6,7 +6,8 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# ----------------------------------------------------
MODULES= \
- hipe_SUITE
+ hipe_SUITE \
+ opt_verify_SUITE
# .erl files for these modules are automatically generated
GEN_MODULES= \
@@ -79,4 +80,4 @@ release_tests_spec: make_emakefile
@tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
cd "$(RELSYSDIR)";\
erlc hipe_testsuite_driver.erl;\
- erl -noshell -run hipe_testsuite_driver create_all_suites -s erlang halt
+ erl -noshell -run hipe_testsuite_driver create_all_suites $(GEN_MODULES) -s erlang halt
diff --git a/lib/hipe/test/hipe_testsuite_driver.erl b/lib/hipe/test/hipe_testsuite_driver.erl
index 64c5c0a7c9..03ec7adfd0 100644
--- a/lib/hipe/test/hipe_testsuite_driver.erl
+++ b/lib/hipe/test/hipe_testsuite_driver.erl
@@ -1,6 +1,6 @@
-module(hipe_testsuite_driver).
--export([create_all_suites/0, run/3]).
+-export([create_all_suites/1, run/3]).
-include_lib("kernel/include/file.hrl").
@@ -16,25 +16,17 @@
outputfile :: file:io_device(),
testcases :: [testcase()]}).
--spec create_all_suites() -> 'ok'.
+-spec create_all_suites([string()]) -> 'ok'.
-create_all_suites() ->
- {ok, Cwd} = file:get_cwd(),
- Suites = get_suites(Cwd),
+create_all_suites(SuitesWithSuiteSuffix) ->
+ Suites = get_suites(SuitesWithSuiteSuffix),
lists:foreach(fun create_suite/1, Suites).
--spec get_suites(file:filename()) -> [string()].
+-spec get_suites([string()]) -> [string()].
-get_suites(Dir) ->
- case file:list_dir(Dir) of
- {error, _} -> [];
- {ok, Filenames} ->
- FullFilenames = [filename:join(Dir, F) || F <- Filenames],
- Dirs = [suffix(filename:basename(F), ?suite_data) ||
- F <- FullFilenames,
- file_type(F) =:= {ok, 'directory'}],
- [S || {yes, S} <- Dirs]
- end.
+get_suites(SuitesWithSuiteSuffix) ->
+ Prefixes = [suffix(F, ?suite_suffix) || F <- SuitesWithSuiteSuffix],
+ [S || {yes, S} <- Prefixes].
suffix(String, Suffix) ->
case string:rstr(String, Suffix) of
diff --git a/lib/hipe/test/opt_verify_SUITE.erl b/lib/hipe/test/opt_verify_SUITE.erl
new file mode 100644
index 0000000000..61952e81d7
--- /dev/null
+++ b/lib/hipe/test/opt_verify_SUITE.erl
@@ -0,0 +1,62 @@
+-module(opt_verify_SUITE).
+
+-compile([export_all]).
+
+all() ->
+ [call_elim].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ case erlang:system_info(hipe_architecture) of
+ undefined -> {skip, "HiPE not available or enabled"};
+ _ -> Config
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+call_elim_test_file(Config, FileName, Option) ->
+ PrivDir = test_server:lookup_config(priv_dir, Config),
+ TempOut = test_server:temp_name(filename:join(PrivDir, "call_elim_out")),
+ {ok, TestCase} = compile:file(FileName),
+ {ok, TestCase} = hipe:c(TestCase, [Option, {pp_range_icode, {file, TempOut}}]),
+ {ok, Icode} = file:read_file(TempOut),
+ ok = file:delete(TempOut),
+ Icode.
+
+substring_count(Icode, Substring) ->
+ substring_count(Icode, Substring, 0).
+substring_count(Icode, Substring, N) ->
+ case string:str(Icode, Substring) of
+ 0 -> N;
+ I -> substring_count(lists:nthtail(I, Icode), Substring, N+1)
+ end.
+
+call_elim() ->
+ [{doc, "Test that the call elimination optimization pass is ok"}].
+call_elim(Config) ->
+ DataDir = test_server:lookup_config(data_dir, Config),
+ F1 = filename:join(DataDir, "call_elim_test.erl"),
+ Icode1 = call_elim_test_file(Config, F1, icode_call_elim),
+ 0 = substring_count(binary:bin_to_list(Icode1), "is_key"),
+ Icode2 = call_elim_test_file(Config, F1, no_icode_call_elim),
+ true = (0 /= substring_count(binary:bin_to_list(Icode2), "is_key")),
+ F2 = filename:join(DataDir, "call_elim_test_branches_no_opt_poss.erl"),
+ Icode3 = call_elim_test_file(Config, F2, icode_call_elim),
+ 3 = substring_count(binary:bin_to_list(Icode3), "is_key"),
+ Icode4 = call_elim_test_file(Config, F2, no_icode_call_elim),
+ 3 = substring_count(binary:bin_to_list(Icode4), "is_key"),
+ F3 = filename:join(DataDir, "call_elim_test_branches_opt_poss.erl"),
+ Icode5 = call_elim_test_file(Config, F3, icode_call_elim),
+ 0 = substring_count(binary:bin_to_list(Icode5), "is_key"),
+ Icode6 = call_elim_test_file(Config, F3, no_icode_call_elim),
+ 3 = substring_count(binary:bin_to_list(Icode6), "is_key"),
+ ok.
diff --git a/lib/hipe/test/opt_verify_SUITE_data/call_elim_test.erl b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test.erl
new file mode 100644
index 0000000000..8b725f8ffe
--- /dev/null
+++ b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test.erl
@@ -0,0 +1,12 @@
+-module(call_elim_test).
+
+-export([test/0]).
+
+test() ->
+ true = has_1_field(#{1=>true}),
+ true = has_1_field(#{1=>"hej", b=>2}),
+ true = has_1_field(#{b=>3, 1=>4}),
+ ok.
+
+has_1_field(#{1:=_}) -> true;
+has_1_field(#{}) -> false.
diff --git a/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_no_opt_poss.erl b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_no_opt_poss.erl
new file mode 100644
index 0000000000..7ffae86797
--- /dev/null
+++ b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_no_opt_poss.erl
@@ -0,0 +1,32 @@
+-module(call_elim_test_branches_no_opt_poss).
+
+-export([test/1]).
+
+test(A) ->
+ if A > 0 ->
+ false = has_a_field(#{b=>true}),
+ true = has_a_field(#{b=>1, a=>"2"}),
+ false = has_a_field(#{b=>5, c=>4}),
+ false = has_tuple_field(#{{ab, 2}=><<"qq">>, 1 =>0}),
+ false = has_tuple_field(#{up =>down, {ab, 2}=>[]}),
+ false = has_tuple_field(#{{ab, 2}=>42});
+ A =< 0 ->
+ true = has_a_field(#{a=>q, 'A' =>nej}),
+ true = has_a_field(#{a=>"hej", false=>true}),
+ true = has_a_field(#{a=>3}),
+ true = has_tuple_field(#{{ab, 1}=>q, 'A' =>nej}),
+ true = has_tuple_field(#{{ab, 1}=>"hej", false=>true}),
+ true = has_tuple_field(#{{ab, 1}=>3})
+ end,
+ true = has_nil_field(#{[] =>3, b=>"seven"}),
+ true = has_nil_field(#{"seventeen"=>17}),
+ ok.
+
+has_tuple_field(#{{ab, 1}:=_}) -> true;
+has_tuple_field(#{}) -> false.
+
+has_a_field(#{a:=_}) -> true;
+has_a_field(#{}) -> false.
+
+has_nil_field(#{[]:=_}) -> true;
+has_nil_field(#{}) -> false.
diff --git a/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_opt_poss.erl b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_opt_poss.erl
new file mode 100644
index 0000000000..c8ddfa1e75
--- /dev/null
+++ b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_opt_poss.erl
@@ -0,0 +1,32 @@
+-module(call_elim_test_branches_opt_poss).
+
+-export([test/1]).
+
+test(A) ->
+ if A > 0 ->
+ true = has_a_field(#{a=>true}),
+ true = has_a_field(#{b=>1, a=>"2"}),
+ true = has_a_field(#{a=>5, c=>4}),
+ true = has_tuple_field(#{{ab, 1}=><<"qq">>, 1 =>0}),
+ true = has_tuple_field(#{up =>down, {ab, 1}=>[]}),
+ true = has_tuple_field(#{{ab, 1}=>42});
+ A =< 0 ->
+ true = has_a_field(#{a=>q, 'A' =>nej}),
+ true = has_a_field(#{a=>"hej", false=>true}),
+ true = has_a_field(#{a=>3}),
+ true = has_tuple_field(#{{ab, 1}=>q, 'A' =>nej}),
+ true = has_tuple_field(#{{ab, 1}=>"hej", false=>true}),
+ true = has_tuple_field(#{{ab, 1}=>3})
+ end,
+ true = has_nil_field(#{[] =>3, b =>"seven"}),
+ true = has_nil_field(#{"seventeen"=>17, []=>nil}),
+ ok.
+
+has_tuple_field(#{{ab, 1}:=_}) -> true;
+has_tuple_field(#{}) -> false.
+
+has_a_field(#{a:=_}) -> true;
+has_a_field(#{}) -> false.
+
+has_nil_field(#{[]:=_}) -> true;
+has_nil_field(#{}) -> false.
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index bca04aa244..5ff167bcb3 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -207,6 +207,10 @@ fe80::204:acff:fe17:bf38
<desc>
<p>Returns a <c>hostent</c> record for the host with the specified
hostname.</p>
+ <p>If resolver option <c>inet6</c> is <c>true</c>,
+ an IPv6 address is looked up. If that fails,
+ the IPv4 address is looked up and returned on
+ IPv6-mapped IPv4 format.</p>
</desc>
</func>
@@ -1267,4 +1271,3 @@ inet:setopts(Sock,[{raw,6,8,<<30:32/native>>}]),]]></code>
</list>
</section>
</erlref>
-
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index c1ae99ea24..713a9cf725 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -439,7 +439,12 @@ getstat(Socket,What) ->
Hostent :: hostent().
gethostbyname(Name) ->
- gethostbyname_tm(Name, inet, false).
+ case inet_db:res_option(inet6) of
+ true ->
+ gethostbyname_tm(Name, inet6, false);
+ false ->
+ gethostbyname_tm(Name, inet, false)
+ end.
-spec gethostbyname(Hostname, Family) ->
{ok, Hostent} | {error, posix()} when
diff --git a/lib/observer/doc/src/observer_ug.xml b/lib/observer/doc/src/observer_ug.xml
index ca354df864..6eb72f3e58 100644
--- a/lib/observer/doc/src/observer_ug.xml
+++ b/lib/observer/doc/src/observer_ug.xml
@@ -168,7 +168,7 @@
<item><p>The length of the message queue for the process.</p></item>
</taglist>
- <p>Option <em>Process info</em> opens a detailed information window on the selected process,
+ <p>Option <em>Process info</em> opens a detailed information window on the process under the mouse pointer,
including the following:</p>
<taglist>
<tag>Process Information</tag>
@@ -195,12 +195,53 @@
</p>
</note>
- <p>Option <em>Trace Processes</em> adds the selected process identifiers to tab
+ <p>Option <em>Trace selected processes</em> adds the selected process identifiers to tab
<em>Trace Overview</em> plus the node that the processes reside on.
</p>
- <p>Option <em>Trace Named Processes</em> adds the registered name of the processes. This can be
+ <p>Option <em>Trace selected processes by name</em> adds the registered name of the processes. This can be
useful when tracing is done on many nodes, as processes with that name are then traced on
all traced nodes.</p>
+ <p>Option <em>Kill process</em> brutally kills the processes under
+ the mouse pointer by sending an exit signal with
+ reason <c>kill</c>.</p>
+
+ </section>
+
+ <section>
+ <title>Ports Tab</title>
+ <p>Tab <em>Ports</em> lists port information in columns.
+ For each port the following information is displayed:
+ </p>
+ <taglist>
+ <tag>Id</tag>
+ <item><p>The port identifier.</p></item>
+ <tag>Connected</tag>
+ <item><p>The process identifier for the process that owns the
+ port.</p></item>
+ <tag>Name</tag>
+ <item><p>The registered name of the port, if any.</p></item>
+ <tag>Controls</tag>
+ <item><p>The name of the command set by <seealso marker="erts:erlang#open_port-2"><c>erlang:open_port/2</c></seealso>.</p></item>
+ <tag>Slot</tag>
+ <item><p>The internal index of the port.</p></item>
+ </taglist>
+
+ <p>Option <em>Port info</em> opens a detailed information window
+ for the port under the mouse pointer. In addition to the
+ information above, it also shows links and monitors.</p>
+
+ <p>Option <em>Trace selected ports</em> adds the selected port
+ identifiers, and the nodes that the ports reside on,
+ to tab <em>Trace Overview</em>.</p>
+
+ <p>Option <em>Trace selected ports by name</em> adds the
+ registered name of the port to tab <em>Trace Overview</em>. This
+ can be useful when tracing is done on many nodes, as ports with
+ that name are then traced on all traced nodes.</p>
+
+ <p>Option <em>Close</em>
+ executes <seealso marker="erts:erlang#port_close-1"><c>erlang:port_close/1</c></seealso>
+ on the port under the mouse pointer.</p>
</section>
@@ -211,8 +252,11 @@
applications are not diplayed. Use menu <em>View</em> to view "system"
ETS tables, unreadable ETS tables, or Mnesia tables.
</p>
- <p>Double-click to view the table content. To view table information, select the table
- and activate menu <em>View &gt; Table information</em>.</p>
+ <p>Double-click to view the table content, or right-click and
+ select option <em>Show Table Content</em>. To view table
+ information, select the table and activate menu <em>View &gt;
+ Table information</em>, or right-click and select option <em>Table
+ info</em>.</p>
<p>You can use <seealso marker="stdlib:re">regular
expressions</seealso> and search for objects, and edit or delete them.
</p>
@@ -220,11 +264,12 @@
<section>
<title>Trace Overview Tab</title>
- <p>Tab <em>Trace Overview</em> handles tracing. Trace
- by selecting the processes to be traced and how to trace
- them. You can trace messages, function calls, and events, where
- events are process-related events such as <c>spawn</c>,
- <c>exit</c>, and many others.
+ <p>Tab <em>Trace Overview</em> handles tracing. Trace by selecting
+ the processes or ports to be traced and how to trace them. For
+ processes, you can trace messages, function calls, scheduling,
+ garbage collections, and process-related events such
+ as <c>spawn</c>, <c>exit</c>, and many others. For ports, you can
+ trace messages, scheduling and port-related events.
</p>
<p>To trace function calls, you also need to set up
@@ -234,27 +279,51 @@
specifications can also be used to trigger more information
in the trace messages.
</p>
- <note><p>Trace patterns only apply to the traced processes.</p></note>
+
+ <p>You can also set match specifications on messages. By default,
+ if tracing messages, all messages sent and/or received by the
+ process or port are traced. Match specifications can be used to
+ reduce the number of traced messages and/or to trigger more
+ information in the trace messages.</p>
+
+ <note><p>Trace patterns only apply to the traced processes and
+ ports.</p></note>
<p>
- Processes are added from the <em>Applications</em> or <em>Processes</em> tabs.
- A special <em>new</em> identifier, meaning all processes spawned after trace
- start, can be added with button <em>Add 'new' Process</em>.
+ Processes are added from the <em>Applications</em>
+ or <em>Processes</em> tabs. Ports are added from
+ the <em>Ports</em> tab. A special <em>new</em> identifier,
+ meaning all processes, or ports, started after trace start, can
+ be added with buttons <em>Add 'new' Processes</em> and <em>Add
+ 'new' Ports</em>, respecively.
</p>
<p>
- When adding processes, a window with trace options is displayed. The chosen
- options are set for the selected processes.
- Process options can be changed by right-clicking a process.
+ When adding processes or ports, a window with trace options is
+ displayed. The chosen options are set for the selected
+ processes/ports. To change the options, right-click the process
+ or port and select <em>Edit process options</em>. To remove a
+ process or port from the list, right-click and select <em>Remove
+ process</em> or <em>Remove port</em>, respectively.
</p>
<p>
- Processes added by process identifiers add the nodes these
- processes reside on in the node list. More nodes can be added by clicking
- button <em>Add Nodes</em>.
+ Processes and ports added by process/port identifiers add the
+ nodes these processes/ports reside on in the node list. More
+ nodes can be added by clicking button <em>Add Nodes</em>, or by
+ right-clicking in the <em>Nodes</em> list and select <em>Add
+ Nodes</em>. To remove nodes, select them, then right-click and
+ choose <em>Remove nodes</em>.
</p>
<p>
If function calls are traced, trace patterns must be added by clicking button
<em>Add Trace Pattern</em>. Select a module, function(s), and a match specification.
- If no functions are selected, all functions in the module are traced.
+ If no functions are selected, all functions in the module are traced.</p>
+ <p>
+ Trace patterns can also be added for traced messages. Click
+ button <em>Add Trace Pattern</em> and select <em>Messages
+ sent</em> or <em>Messages received</em>, and a match
+ specification.
+ </p>
+ <p>
A few basic match specifications are provided in the tool, and
you can provide your own match specifications. The syntax of match
specifications is described in the <seealso
diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml
index 2b637551db..94ecef24b4 100644
--- a/lib/observer/doc/src/ttb.xml
+++ b/lib/observer/doc/src/ttb.xml
@@ -229,27 +229,33 @@ ttb:p(all, call).</input></pre>
</func>
<func>
- <name>p(Procs,Flags) -> Return</name>
- <fsummary>Set the specified trace flags on the specified processes.</fsummary>
+ <name>p(Item,Flags) -> Return</name>
+ <fsummary>Set the specified trace flags on the specified processes or ports.</fsummary>
<type>
- <v>Return = {ok,[{Procs,MatchDesc}]}</v>
- <v>Procs = Process | [Process] | all | new | existing</v>
- <v>Process = pid() | atom() | {global,atom()}</v>
+ <v>Return = {ok,[{Item,MatchDesc}]}</v>
+ <v>Items = Item | [Item]</v>
+ <v>Item = pid() | port() | RegName | {global,GlobalRegName} |
+ all | processes | ports |
+ existing | existing_processes | existing_ports |
+ new | new_processes | new_ports</v>
+ <v>RegName = atom()</v>
+ <v>GlobalRegName = term()</v>
<v>Flags = Flag | [Flag]</v>
</type>
<desc>
- <p>Sets the specified trace flags on the specified
- processes. Flag <c>timestamp</c> is always turned on.
+ <p>Sets the specified trace flags on the specified processes
+ or ports. Flag <c>timestamp</c> is always turned on.
</p>
<p>See the Reference Manual for module
<seealso marker="runtime_tools:dbg"><c>dbg</c></seealso>
- and the possible trace flags. Parameter
+ for the possible trace flags. Parameter
<c>MatchDesc</c> is the same as returned from
<c>dbg:p/2</c>.</p>
<p>Processes can be specified as registered names, globally
- registered names, or process identifiers. If a registered name
- is specified, the flags are set on processes with this name on all
- active nodes.</p>
+ registered names, or process identifiers. Ports can be
+ specified as registered names or port identifiers. If a
+ registered name is specified, the flags are set on
+ processes/ports with this name on all active nodes.</p>
<p>Issuing this command starts the timer for this trace if option
<c>timer</c> is specified with <c>tracer/2</c>.
</p>
@@ -257,17 +263,23 @@ ttb:p(all, call).</input></pre>
</func>
<func>
- <name>tp, tpl, ctp, ctpl, ctpg</name>
+ <name>tp, tpl, tpe, ctp, ctpl, ctpg, ctpe</name>
<fsummary>Set and clear trace patterns.</fsummary>
<desc>
- <p>These functions are to be used with
- trace flag <c>call</c> for setting and clearing trace
- patterns. When trace flag <c>call</c> is set on a process,
+ <p>These functions are to be used with trace
+ flag <c>call</c>, <c>send</c>, and <c>'receive'</c> for
+ setting and clearing trace patterns.</p>
+ <p>When trace flag <c>call</c> is set on a process,
function calls are traced on that process if a trace
- pattern is set for the called function. Trace patterns
- specify how to trace a function by using match
- specifications. Match specifications are described in the
- <seealso marker="erts:users_guide"><c>ERTS User's Guide</c></seealso>.
+ pattern is set for the called function.</p>
+ <p>The <c>send</c> and <c>'receive'</c> flags enable tracing
+ of all messages sent and received by the process/port. Trace
+ patterns set with <c>tpe</c> may limit traced messages based
+ on the message content, the sender, and/or the receiver.</p>
+ <p>Trace patterns specify how to trace a function or a message
+ by using match specifications. Match specifications are
+ described in the
+ <seealso marker="erts:match_spec"><c>ERTS User's Guide</c></seealso>.
</p>
<p>These functions are equivalent to the corresponding
functions in module
@@ -284,6 +296,8 @@ ttb:p(all, call).</input></pre>
<item><p>Sets trace patterns on global function calls.</p></item>
<tag><c>tpl</c></tag>
<item><p>Sets trace patterns on local and global function calls.</p></item>
+ <tag><c>tpe</c></tag>
+ <item><p>Sets trace patterns on messages.</p></item>
<tag><c>ctp</c></tag>
<item><p>Clears trace patterns on local and global function
calls.</p></item>
@@ -291,13 +305,15 @@ ttb:p(all, call).</input></pre>
<item><p>Clears trace patterns on local function calls.</p></item>
<tag><c>ctpg</c></tag>
<item><p>Clears trace patterns on global function calls.</p></item>
+ <tag><c>ctpe</c></tag>
+ <item><p>Clears trace patterns on messages.</p></item>
</taglist>
<p>With <c>tp</c> and <c>tpl</c>, one of the match specification shortcuts
can be used (for example, <c>ttb:tp(foo_module, caller)</c>).</p>
<p>The shortcuts are as follows:</p>
<list type="bulleted">
<item><c>return</c> - for <c>[{'_',[],[{return_trace}]}]</c>
- (report the return value)</item>
+ (report the return value from a traced function)</item>
<item><c>caller</c> - for <c>[{'_',[],[{message,{caller}}]}]</c>
(report the calling function)</item>
<item><c>{codestr, Str}</c> - for <c>dbg:fun2ms/1</c> arguments
diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile
index 85dc5933c1..dd7831fa2b 100644
--- a/lib/observer/src/Makefile
+++ b/lib/observer/src/Makefile
@@ -67,6 +67,7 @@ MODULES= \
observer_html_lib \
observer_lib \
observer_perf_wx \
+ observer_port_wx \
observer_pro_wx \
observer_procinfo \
observer_sys_wx \
diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src
index 5ddf65fa59..3a5bd172e7 100644
--- a/lib/observer/src/observer.app.src
+++ b/lib/observer/src/observer.app.src
@@ -51,6 +51,7 @@
observer_html_lib,
observer_lib,
observer_perf_wx,
+ observer_port_wx,
observer_pro_wx,
observer_procinfo,
observer_sys_wx,
diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl
index cef83037d0..936b2783e2 100644
--- a/lib/observer/src/observer_app_wx.erl
+++ b/lib/observer/src/observer_app_wx.erl
@@ -221,21 +221,21 @@ handle_event(#wx{id=?ID_PROC_KILL, event=#wxCommand{type=command_menu_selected}}
%%% Trace api
handle_event(#wx{id=?ID_TRACE_PID, event=#wxCommand{type=command_menu_selected}},
State = #state{sel={Box,_}}) ->
- observer_trace_wx:add_processes(observer_wx:get_tracer(), [box_to_pid(Box)]),
+ observer_trace_wx:add_processes([box_to_pid(Box)]),
{noreply, State};
handle_event(#wx{id=?ID_TRACE_NAME, event=#wxCommand{type=command_menu_selected}},
State = #state{sel={Box,_}}) ->
- observer_trace_wx:add_processes(observer_wx:get_tracer(), [box_to_reg(Box)]),
+ observer_trace_wx:add_processes([box_to_reg(Box)]),
{noreply, State};
handle_event(#wx{id=?ID_TRACE_TREE_PIDS, event=#wxCommand{type=command_menu_selected}},
State = #state{sel=Sel}) ->
Get = fun(Box) -> box_to_pid(Box) end,
- observer_trace_wx:add_processes(observer_wx:get_tracer(), tree_map(Sel, Get)),
+ observer_trace_wx:add_processes(tree_map(Sel, Get)),
{noreply, State};
handle_event(#wx{id=?ID_TRACE_TREE_NAMES, event=#wxCommand{type=command_menu_selected}},
State = #state{sel=Sel}) ->
Get = fun(Box) -> box_to_reg(Box) end,
- observer_trace_wx:add_processes(observer_wx:get_tracer(), tree_map(Sel, Get)),
+ observer_trace_wx:add_processes(tree_map(Sel, Get)),
{noreply, State};
handle_event(Event, _State) ->
diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl
new file mode 100644
index 0000000000..3b788642cc
--- /dev/null
+++ b/lib/observer/src/observer_port_wx.erl
@@ -0,0 +1,479 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+-module(observer_port_wx).
+
+-export([start_link/2]).
+
+%% wx_object callbacks
+-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
+ handle_event/2, handle_sync_event/3, handle_cast/2]).
+
+-behaviour(wx_object).
+-include_lib("wx/include/wx.hrl").
+-include("observer_defs.hrl").
+
+-define(GRID, 300).
+-define(ID_REFRESH, 301).
+-define(ID_REFRESH_INTERVAL, 302).
+-define(ID_PORT_INFO, 303).
+-define(ID_PORT_INFO_SELECTED, 304).
+-define(ID_TRACE_PORTS, 305).
+-define(ID_TRACE_NAMES, 306).
+-define(ID_TRACE_NEW, 307).
+-define(ID_TRACE_ALL, 308).
+-define(ID_CLOSE_PORT, 309).
+
+-define(TRACE_PORTS_STR, "Trace selected ports").
+-define(TRACE_NAMES_STR, "Trace selected ports, "
+ "if a process have a registered name "
+ "processes with same name will be traced on all nodes").
+
+-record(port,
+ {id,
+ connected,
+ name,
+ controls,
+ slot,
+ id_str,
+ links,
+ monitors}).
+
+-record(opt, {sort_key=2,
+ sort_incr=true
+ }).
+
+-record(state,
+ {
+ parent,
+ grid,
+ panel,
+ node=node(),
+ opt=#opt{},
+ right_clicked_port,
+ ports,
+ timer,
+ open_wins=[]
+ }).
+
+start_link(Notebook, Parent) ->
+ wx_object:start_link(?MODULE, [Notebook, Parent], []).
+
+init([Notebook, Parent]) ->
+ Panel = wxPanel:new(Notebook),
+ Sizer = wxBoxSizer:new(?wxVERTICAL),
+ Style = ?wxLC_REPORT bor ?wxLC_HRULES,
+ Grid = wxListCtrl:new(Panel, [{winid, ?GRID}, {style, Style}]),
+ wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL},
+ {proportion, 1}, {border, 5}]),
+ wxWindow:setSizer(Panel, Sizer),
+ Li = wxListItem:new(),
+ AddListEntry = fun({Name, Align, DefSize}, Col) ->
+ wxListItem:setText(Li, Name),
+ wxListItem:setAlign(Li, Align),
+ wxListCtrl:insertColumn(Grid, Col, Li),
+ wxListCtrl:setColumnWidth(Grid, Col, DefSize),
+ Col + 1
+ end,
+ ListItems = [{"Id", ?wxLIST_FORMAT_LEFT, 150},
+ {"Connected", ?wxLIST_FORMAT_LEFT, 150},
+ {"Name", ?wxLIST_FORMAT_LEFT, 150},
+ {"Controls", ?wxLIST_FORMAT_LEFT, 200},
+ {"Slot", ?wxLIST_FORMAT_RIGHT, 50}],
+ lists:foldl(AddListEntry, 0, ListItems),
+ wxListItem:destroy(Li),
+
+ wxListCtrl:connect(Grid, command_list_item_right_click),
+ wxListCtrl:connect(Grid, command_list_item_activated),
+ wxListCtrl:connect(Grid, command_list_col_click),
+ wxListCtrl:connect(Grid, size, [{skip, true}]),
+
+ wxWindow:setFocus(Grid),
+ {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer={false, 10}}}.
+
+handle_event(#wx{id=?ID_REFRESH},
+ State = #state{node=Node, grid=Grid, opt=Opt}) ->
+ Ports0 = get_ports(Node),
+ Ports = update_grid(Grid, Opt, Ports0),
+ {noreply, State#state{ports=Ports}};
+
+handle_event(#wx{obj=Obj, event=#wxClose{}}, #state{open_wins=Opened} = State) ->
+ NewOpened =
+ case lists:keytake(Obj,2,Opened) of
+ false -> Opened;
+ {value,_,Rest} -> Rest
+ end,
+ {noreply, State#state{open_wins=NewOpened}};
+
+handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}},
+ State = #state{node=Node, grid=Grid,
+ opt=Opt0=#opt{sort_key=Key, sort_incr=Bool}}) ->
+ Opt = case Col+2 of
+ Key -> Opt0#opt{sort_incr=not Bool};
+ NewKey -> Opt0#opt{sort_key=NewKey}
+ end,
+ Ports0 = get_ports(Node),
+ Ports = update_grid(Grid, Opt, Ports0),
+ wxWindow:setFocus(Grid),
+ {noreply, State#state{opt=Opt, ports=Ports}};
+
+handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) ->
+ observer_lib:set_listctrl_col_size(Grid, W),
+ {noreply, State};
+
+handle_event(#wx{event=#wxList{type=command_list_item_activated,
+ itemIndex=Index}},
+ State=#state{grid=Grid, ports=Ports, open_wins=Opened}) ->
+ Port = lists:nth(Index+1, Ports),
+ NewOpened = display_port_info(Grid, Port, Opened),
+ {noreply, State#state{open_wins=NewOpened}};
+
+handle_event(#wx{event=#wxList{type=command_list_item_right_click,
+ itemIndex=Index}},
+ State=#state{panel=Panel, ports=Ports}) ->
+ case Index of
+ -1 ->
+ {noreply, State};
+ _ ->
+ Port = lists:nth(Index+1, Ports),
+ Menu = wxMenu:new(),
+ wxMenu:append(Menu, ?ID_PORT_INFO,
+ "Port info for " ++ erlang:port_to_list(Port#port.id)),
+ wxMenu:append(Menu, ?ID_TRACE_PORTS,
+ "Trace selected ports",
+ [{help, ?TRACE_PORTS_STR}]),
+ wxMenu:append(Menu, ?ID_TRACE_NAMES,
+ "Trace selected ports by name (all nodes)",
+ [{help, ?TRACE_NAMES_STR}]),
+ wxMenu:append(Menu, ?ID_CLOSE_PORT,
+ "Close " ++ erlang:port_to_list(Port#port.id)),
+ wxWindow:popupMenu(Panel, Menu),
+ wxMenu:destroy(Menu),
+ {noreply, State#state{right_clicked_port=Port}}
+ end;
+
+handle_event(#wx{id=?ID_PORT_INFO},
+ State = #state{grid=Grid, right_clicked_port=Port,
+ open_wins=Opened}) ->
+ case Port of
+ undefined ->
+ {noreply, State};
+ _ ->
+ NewOpened = display_port_info(Grid, Port, Opened),
+ {noreply, State#state{right_clicked_port=undefined,
+ open_wins=NewOpened}}
+ end;
+
+handle_event(#wx{id=?ID_PORT_INFO_SELECTED},
+ State = #state{grid=Grid, ports=Ports, open_wins=Opened}) ->
+ case get_selected_items(Grid,Ports) of
+ [] ->
+ observer_wx:create_txt_dialog(State#state.panel, "No selected ports",
+ "Port Info", ?wxICON_EXCLAMATION),
+ {noreply, State};
+ Selected ->
+ NewOpened = lists:foldl(fun(P,O) -> display_port_info(Grid, P, O) end,
+ Opened, Selected),
+ {noreply, State#state{open_wins = NewOpened}}
+ end;
+
+handle_event(#wx{id=?ID_CLOSE_PORT}, State = #state{right_clicked_port=Port}) ->
+ case Port of
+ undefined ->
+ {noreply, State};
+ _ ->
+ erlang:port_close(Port#port.id),
+ {noreply, State#state{right_clicked_port=undefined}}
+ end;
+
+handle_event(#wx{id=?ID_TRACE_PORTS}, #state{grid=Grid, ports=Ports}=State) ->
+ case get_selected_items(Grid, Ports) of
+ [] ->
+ observer_wx:create_txt_dialog(State#state.panel, "No selected ports",
+ "Tracer", ?wxICON_EXCLAMATION);
+ Selected ->
+ SelectedIds = [Port#port.id || Port <- Selected],
+ observer_trace_wx:add_ports(SelectedIds)
+ end,
+ {noreply, State};
+
+handle_event(#wx{id=?ID_TRACE_NAMES}, #state{grid=Grid, ports=Ports}=State) ->
+ case get_selected_items(Grid, Ports) of
+ [] ->
+ observer_wx:create_txt_dialog(State#state.panel, "No selected ports",
+ "Tracer", ?wxICON_EXCLAMATION);
+ Selected ->
+ IdsOrRegs =
+ [case Port#port.name of
+ [] -> Port#port.id;
+ Name -> Name
+ end || Port <- Selected],
+ observer_trace_wx:add_ports(IdsOrRegs)
+ end,
+ {noreply, State};
+
+handle_event(#wx{id=?ID_TRACE_NEW, event=#wxCommand{type=command_menu_selected}}, State) ->
+ observer_trace_wx:add_ports([new_ports]),
+ {noreply, State};
+
+handle_event(#wx{id=?ID_REFRESH_INTERVAL},
+ State = #state{grid=Grid, timer=Timer0}) ->
+ Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60),
+ {noreply, State#state{timer=Timer}};
+
+handle_event(#wx{event=#wxMouse{type=left_down}, userData=TargetPid}, State) ->
+ observer ! {open_link, TargetPid},
+ {noreply, State};
+
+handle_event(#wx{obj=Obj, event=#wxMouse{type=enter_window}}, State) ->
+ wxTextCtrl:setForegroundColour(Obj,{0,0,100,255}),
+ {noreply, State};
+
+handle_event(#wx{obj=Obj, event=#wxMouse{type=leave_window}}, State) ->
+ wxTextCtrl:setForegroundColour(Obj,?wxBLUE),
+ {noreply, State};
+
+handle_event(Event, _State) ->
+ error({unhandled_event, Event}).
+
+handle_sync_event(_Event, _Obj, _State) ->
+ ok.
+
+handle_call(Event, From, _State) ->
+ error({unhandled_call, Event, From}).
+
+handle_cast(Event, _State) ->
+ error({unhandled_cast, Event}).
+
+handle_info({portinfo_open, PortIdStr},
+ State = #state{grid=Grid, ports=Ports, open_wins=Opened}) ->
+ Port = lists:keyfind(PortIdStr,#port.id_str,Ports),
+ NewOpened = display_port_info(Grid, Port, Opened),
+ {noreply, State#state{open_wins = NewOpened}};
+
+handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt,
+ ports=OldPorts}) ->
+ case get_ports(Node) of
+ OldPorts ->
+ %% no change
+ {noreply, State};
+ Ports0 ->
+ Ports = update_grid(Grid, Opt, Ports0),
+ {noreply, State#state{ports=Ports}}
+ end;
+
+handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt,
+ timer=Timer0}) ->
+ Ports0 = get_ports(Node),
+ Ports = update_grid(Grid, Opt, Ports0),
+ wxWindow:setFocus(Grid),
+ create_menus(Parent),
+ Timer = observer_lib:start_timer(Timer0),
+ {noreply, State#state{node=Node, ports=Ports, timer=Timer}};
+
+handle_info(not_active, State = #state{timer = Timer0}) ->
+ Timer = observer_lib:stop_timer(Timer0),
+ {noreply, State#state{timer=Timer}};
+
+handle_info({error, Error}, State) ->
+ handle_error(Error),
+ {noreply, State};
+
+handle_info(_Event, State) ->
+ {noreply, State}.
+
+terminate(_Event, _State) ->
+ ok.
+
+code_change(_, _, State) ->
+ State.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+create_menus(Parent) ->
+ MenuEntries =
+ [{"View",
+ [#create_menu{id = ?ID_PORT_INFO_SELECTED,
+ text = "Port info for selected ports\tCtrl-I"},
+ separator,
+ #create_menu{id = ?ID_REFRESH, text = "Refresh\tCtrl-R"},
+ #create_menu{id = ?ID_REFRESH_INTERVAL, text = "Refresh Interval..."}
+ ]},
+ {"Trace",
+ [#create_menu{id=?ID_TRACE_PORTS, text="Trace selected ports"},
+ #create_menu{id=?ID_TRACE_NAMES, text="Trace selected ports by name (all nodes)"},
+ #create_menu{id=?ID_TRACE_NEW, text="Trace new ports"}
+ ]}
+ ],
+ observer_wx:create_menus(Parent, MenuEntries).
+
+get_ports(Node) ->
+ case get_ports2(Node) of
+ Error = {error, _} ->
+ self() ! Error,
+ [];
+ Res ->
+ Res
+ end.
+get_ports2(Node) ->
+ case rpc:call(Node, observer_backend, get_port_list, []) of
+ {badrpc, Error} ->
+ {error, Error};
+ Error = {error, _} ->
+ Error;
+ Result ->
+ [list_to_portrec(Port) || Port <- Result]
+ end.
+
+list_to_portrec(PL) ->
+ %% PortInfo:
+ %% {registered_name, RegisteredName :: atom()} |
+ %% {id, Index :: integer() >= 0} |
+ %% {connected, Pid :: pid()} |
+ %% {links, Pids :: [pid()]} |
+ %% {name, String :: string()} |
+ %% {input, Bytes :: integer() >= 0} |
+ %% {output, Bytes :: integer() >= 0} |
+ %% {os_pid, OsPid :: integer() >= 0 | undefined},
+ PortId = proplists:get_value(port_id, PL),
+ #port{id = PortId,
+ id_str = erlang:port_to_list(PortId),
+ slot = proplists:get_value(id, PL),
+ connected = proplists:get_value(connected, PL),
+ links = proplists:get_value(links, PL, []),
+ name = proplists:get_value(registered_name, PL, []),
+ monitors = proplists:get_value(monitors, PL, []),
+ controls = proplists:get_value(name, PL)}.
+
+portrec_to_list(#port{id = Id,
+ slot = Slot,
+ connected = Connected,
+ links = Links,
+ name = Name,
+ monitors = Monitors,
+ controls = Controls}) ->
+ [{id,Id},
+ {slot,Slot},
+ {connected,Connected},
+ {links,Links},
+ {name,Name},
+ {monitors,Monitors},
+ {controls,Controls}].
+
+display_port_info(Parent, PortRec, Opened) ->
+ PortIdStr = PortRec#port.id_str,
+ case lists:keyfind(PortIdStr,1,Opened) of
+ false ->
+ Frame = do_display_port_info(Parent, PortRec),
+ [{PortIdStr,Frame}|Opened];
+ {_,Win} ->
+ wxFrame:raise(Win),
+ Opened
+ end.
+
+do_display_port_info(Parent0, PortRec) ->
+ Parent = observer_lib:get_wx_parent(Parent0),
+ Title = "Port Info: " ++ PortRec#port.id_str,
+ Frame = wxMiniFrame:new(Parent, ?wxID_ANY, Title,
+ [{style, ?wxSYSTEM_MENU bor ?wxCAPTION
+ bor ?wxCLOSE_BOX bor ?wxRESIZE_BORDER}]),
+
+ Port = portrec_to_list(PortRec),
+ Fields0 = port_info_fields(Port),
+ {_FPanel, _Sizer, _UpFields} = observer_lib:display_info(Frame, Fields0),
+ wxFrame:center(Frame),
+ wxFrame:connect(Frame, close_window, [{skip, true}]),
+ wxFrame:show(Frame),
+ Frame.
+
+
+port_info_fields(Port) ->
+ Struct =
+ [{"Overview",
+ [{"Name", name},
+ {"Connected", {click,connected}},
+ {"Slot", slot},
+ {"Controls", controls}]},
+ {scroll_boxes,
+ [{"Links",1,{click,links}},
+ {"Monitors",1,{click,filter_monitor_info()}}]}],
+ observer_lib:fill_info(Struct, Port).
+
+filter_monitor_info() ->
+ fun(Data) ->
+ Ms = proplists:get_value(monitors, Data),
+ [Pid || {process, Pid} <- Ms]
+ end.
+
+
+handle_error(Foo) ->
+ Str = io_lib:format("ERROR: ~s~n",[Foo]),
+ observer_lib:display_info_dialog(Str).
+
+update_grid(Grid, Opt, Ports) ->
+ wx:batch(fun() -> update_grid2(Grid, Opt, Ports) end).
+update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Ports) ->
+ wxListCtrl:deleteAllItems(Grid),
+ Update =
+ fun(#port{id = Id,
+ slot = Slot,
+ connected = Connected,
+ name = Name,
+ controls = Ctrl},
+ Row) ->
+ _Item = wxListCtrl:insertItem(Grid, Row, ""),
+ if (Row rem 2) =:= 0 ->
+ wxListCtrl:setItemBackgroundColour(Grid, Row, ?BG_EVEN);
+ true -> ignore
+ end,
+
+ lists:foreach(fun({Col, Val}) ->
+ wxListCtrl:setItem(Grid, Row, Col,
+ observer_lib:to_str(Val))
+ end,
+ [{0,Id},{1,Connected},{2,Name},{3,Ctrl},{4,Slot}]),
+ Row + 1
+ end,
+ PortInfo = case Dir of
+ false -> lists:reverse(lists:keysort(Sort, Ports));
+ true -> lists:keysort(Sort, Ports)
+ end,
+ lists:foldl(Update, 0, PortInfo),
+ PortInfo.
+
+
+get_selected_items(Grid, Data) ->
+ get_indecies(get_selected_items(Grid, -1, []), Data).
+get_selected_items(Grid, Index, ItemAcc) ->
+ Item = wxListCtrl:getNextItem(Grid, Index, [{geometry, ?wxLIST_NEXT_ALL},
+ {state, ?wxLIST_STATE_SELECTED}]),
+ case Item of
+ -1 ->
+ lists:reverse(ItemAcc);
+ _ ->
+ get_selected_items(Grid, Item, [Item | ItemAcc])
+ end.
+
+get_indecies(Items, Data) ->
+ get_indecies(Items, 0, Data).
+get_indecies([I|Rest], I, [H|T]) ->
+ [H|get_indecies(Rest, I+1, T)];
+get_indecies(Rest = [_|_], I, [_|T]) ->
+ get_indecies(Rest, I+1, T);
+get_indecies(_, _, _) ->
+ [].
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index bd914cdf65..ee6829b847 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -83,6 +83,7 @@
timer,
procinfo_menu_pids=[],
sel={[], []},
+ right_clicked_pid,
holder}).
start_link(Notebook, Parent) ->
@@ -303,13 +304,14 @@ handle_event(#wx{id=?ID_REFRESH_INTERVAL},
Timer = observer_lib:interval_dialog(Panel, Timer0, 1, 5*60),
{noreply, State#state{timer=Timer}};
-handle_event(#wx{id=?ID_KILL}, #state{sel={[_|Ids], [ToKill|Pids]}}=State) ->
- exit(ToKill, kill),
- {noreply, State#state{sel={Ids,Pids}}};
+handle_event(#wx{id=?ID_KILL}, #state{right_clicked_pid=Pid, sel=Sel0}=State) ->
+ exit(Pid, kill),
+ Sel = rm_selected(Pid,Sel0),
+ {noreply, State#state{sel=Sel}};
handle_event(#wx{id=?ID_PROC},
- #state{panel=Panel, sel={_, [Pid|_]},procinfo_menu_pids=Opened}=State) ->
+ #state{panel=Panel, right_clicked_pid=Pid, procinfo_menu_pids=Opened}=State) ->
Opened2 = start_procinfo(Pid, Panel, Opened),
{noreply, State#state{procinfo_menu_pids=Opened2}};
@@ -319,7 +321,7 @@ handle_event(#wx{id=?ID_TRACE_PIDS}, #state{sel={_, Pids}, panel=Panel}=State)
observer_wx:create_txt_dialog(Panel, "No selected processes", "Tracer", ?wxICON_EXCLAMATION),
{noreply, State};
Pids ->
- observer_trace_wx:add_processes(observer_wx:get_tracer(), Pids),
+ observer_trace_wx:add_processes(Pids),
{noreply, State}
end;
@@ -330,12 +332,12 @@ handle_event(#wx{id=?ID_TRACE_NAMES}, #state{sel={SelIds,_Pids}, holder=Holder,
{noreply, State};
_ ->
PidsOrReg = call(Holder, {get_name_or_pid, self(), SelIds}),
- observer_trace_wx:add_processes(observer_wx:get_tracer(), PidsOrReg),
+ observer_trace_wx:add_processes(PidsOrReg),
{noreply, State}
end;
handle_event(#wx{id=?ID_TRACE_NEW, event=#wxCommand{type=command_menu_selected}}, State) ->
- observer_trace_wx:add_processes(observer_wx:get_tracer(), [new]),
+ observer_trace_wx:add_processes([new_processes]),
{noreply, State};
handle_event(#wx{event=#wxSize{size={W,_}}},
@@ -347,20 +349,26 @@ handle_event(#wx{event=#wxList{type=command_list_item_right_click,
itemIndex=Row}},
#state{panel=Panel, holder=Holder}=State) ->
- case call(Holder, {get_row, self(), Row, pid}) of
- {error, undefined} ->
- undefined;
- {ok, _} ->
- Menu = wxMenu:new(),
- wxMenu:append(Menu, ?ID_PROC, "Process info"),
- wxMenu:append(Menu, ?ID_TRACE_PIDS, "Trace processes", [{help, ?TRACE_PIDS_STR}]),
- wxMenu:append(Menu, ?ID_TRACE_NAMES, "Trace named processes (all nodes)",
- [{help, ?TRACE_NAMES_STR}]),
- wxMenu:append(Menu, ?ID_KILL, "Kill Process"),
- wxWindow:popupMenu(Panel, Menu),
- wxMenu:destroy(Menu)
- end,
- {noreply, State};
+ Pid =
+ case call(Holder, {get_row, self(), Row, pid}) of
+ {error, undefined} ->
+ undefined;
+ {ok, P} ->
+ Menu = wxMenu:new(),
+ wxMenu:append(Menu, ?ID_PROC,
+ "Process info for " ++ pid_to_list(P)),
+ wxMenu:append(Menu, ?ID_TRACE_PIDS,
+ "Trace selected processes",
+ [{help, ?TRACE_PIDS_STR}]),
+ wxMenu:append(Menu, ?ID_TRACE_NAMES,
+ "Trace selected processes by name (all nodes)",
+ [{help, ?TRACE_NAMES_STR}]),
+ wxMenu:append(Menu, ?ID_KILL, "Kill process " ++ pid_to_list(P)),
+ wxWindow:popupMenu(Panel, Menu),
+ wxMenu:destroy(Menu),
+ P
+ end,
+ {noreply, State#state{right_clicked_pid=Pid}};
handle_event(#wx{event=#wxList{type=command_list_item_focused,
itemIndex=Row}},
@@ -432,6 +440,17 @@ set_focus([Old|_], [New|_], Grid) ->
wxListCtrl:setItemState(Grid, Old, 0, ?wxLIST_STATE_FOCUSED),
wxListCtrl:setItemState(Grid, New, 16#FFFF, ?wxLIST_STATE_FOCUSED).
+rm_selected(Pid, {Ids, Pids}) ->
+ rm_selected(Pid, Ids, Pids, [], []).
+
+rm_selected(Pid, [_Id|Ids], [Pid|Pids], AccIds, AccPids) ->
+ {lists:reverse(AccIds)++Ids,lists:reverse(AccPids)++Pids};
+rm_selected(Pid, [Id|Ids], [OtherPid|Pids], AccIds, AccPids) ->
+ rm_selected(Pid, Ids, Pids, [Id|AccIds], [OtherPid|AccPids]);
+rm_selected(_, [], [], AccIds, AccPids) ->
+ {lists:reverse(AccIds), lists:reverse(AccPids)}.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%TABLE HOLDER%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init_table_holder(Parent, Attrs) ->
diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl
index 9c0243e4a7..af90e2100c 100644
--- a/lib/observer/src/observer_trace_wx.erl
+++ b/lib/observer/src/observer_trace_wx.erl
@@ -19,7 +19,7 @@
-module(observer_trace_wx).
--export([start_link/2, add_processes/2]).
+-export([start_link/2, add_processes/1, add_ports/1]).
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
handle_event/2, handle_cast/2]).
@@ -31,11 +31,15 @@
-define(SAVE_TRACEOPTS, 305).
-define(LOAD_TRACEOPTS, 306).
-define(TOGGLE_TRACE, 307).
--define(ADD_NEW, 308).
--define(ADD_TP, 309).
--define(TRACE_OUTPUT, 310).
--define(TRACE_DEFMS, 311).
--define(TRACE_DEFPS, 312).
+-define(ADD_NEW_PROCS, 308).
+-define(ADD_NEW_PORTS, 309).
+-define(ADD_TP, 310).
+-define(TRACE_OUTPUT, 311).
+-define(DEF_MS_FUNCS, 312).
+-define(DEF_MS_SEND, 313).
+-define(DEF_MS_RECV, 314).
+-define(DEF_PROC_OPTS, 315).
+-define(DEF_PORT_OPTS, 316).
-define(NODES_WIN, 330).
-define(ADD_NODES, 331).
@@ -45,36 +49,53 @@
-define(EDIT_PROCS, 341).
-define(REMOVE_PROCS, 342).
--define(MODULES_WIN, 350).
+-define(PORT_WIN, 350).
+-define(EDIT_PORTS, 351).
+-define(REMOVE_PORTS, 352).
--define(FUNCS_WIN, 360).
--define(EDIT_FUNCS_MS, 361).
--define(REMOVE_FUNCS_MS, 362).
+-define(MODULES_WIN, 360).
+-define(REMOVE_MOD_MS, 361).
--define(LOG_WIN, 370).
--define(LOG_SAVE, 321).
--define(LOG_CLEAR, 322).
+-define(FUNCS_WIN, 370).
+-define(EDIT_FUNCS_MS, 371).
+-define(REMOVE_FUNCS_MS, 372).
+
+-define(LOG_WIN, 380).
+-define(LOG_SAVE, 381).
+-define(LOG_CLEAR, 382).
+
+-define(NO_NODES_HELP,"Right click to add nodes").
+-define(NODES_HELP,"Select nodes to see traced processes and ports").
+-define(NO_P_HELP,"Add items from Processes/Ports tab").
+-define(P_HELP,"Select nodes to see traced processes and ports").
+-define(NO_TP_HELP,"Add trace pattern with button below").
+-define(TP_HELP,"Select module to see trace patterns").
-record(state,
{parent,
panel,
- n_view, p_view, m_view, f_view, %% The listCtrl's
+ n_view, proc_view, port_view, m_view, f_view, %% The listCtrl's
logwin, %% The latest log window
nodes = [],
toggle_button,
- tpids = [], %% #tpid
- def_trace_opts = [],
+ tpids = [], % #titem
+ tports = [], % #titem
+ def_proc_flags = [],
+ def_port_flags = [],
output = [],
tpatterns = dict:new(), % Key =:= Module::atom, Value =:= {M, F, A, MatchSpec}
match_specs = []}). % [ #match_spec{} ]
--record(tpid, {pid, opts}).
+-record(titem, {id, opts}).
start_link(Notebook, ParentPid) ->
wx_object:start_link(?MODULE, [Notebook, ParentPid], []).
-add_processes(Tracer, Pids) when is_list(Pids) ->
- wx_object:cast(Tracer, {add_processes, Pids}).
+add_processes(Pids) when is_list(Pids) ->
+ wx_object:cast(observer_wx:get_tracer(), {add_processes, Pids}).
+
+add_ports(Ports) when is_list(Ports) ->
+ wx_object:cast(observer_wx:get_tracer(), {add_ports, Ports}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -87,11 +108,13 @@ create_window(Notebook, ParentPid) ->
Sizer = wxBoxSizer:new(?wxVERTICAL),
Splitter = wxSplitterWindow:new(Panel, [{size, wxWindow:getClientSize(Panel)},
{style, ?SASH_STYLE}]),
- {NodeProcView, NodeView, ProcessView} = create_process_view(Splitter),
+ {NodeProcView, NodeView, ProcessView, PortView} =
+ create_proc_port_view(Splitter),
{MatchSpecView,ModView,FuncView} = create_matchspec_view(Splitter),
wxSplitterWindow:setSashGravity(Splitter, 0.5),
wxSplitterWindow:setMinimumPaneSize(Splitter,50),
- wxSplitterWindow:splitHorizontally(Splitter, NodeProcView, MatchSpecView),
+ wxSplitterWindow:splitHorizontally(Splitter, NodeProcView, MatchSpecView,
+ [{sashPosition,368}]),
wxSizer:add(Sizer, Splitter, [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}, {proportion, 1}]),
%% Buttons
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
@@ -99,7 +122,8 @@ create_window(Notebook, ParentPid) ->
wxSizer:add(Buttons, ToggleButton, [{flag, ?wxALIGN_CENTER_VERTICAL}]),
wxSizer:addSpacer(Buttons, 15),
wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NODES, [{label, "Add Nodes"}])),
- wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NEW, [{label, "Add 'new' Process"}])),
+ wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NEW_PROCS, [{label, "Add 'new' Processes"}])),
+ wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NEW_PORTS, [{label, "Add 'new' Ports"}])),
wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_TP, [{label, "Add Trace Pattern"}])),
wxMenu:connect(Panel, command_togglebutton_clicked, [{skip, true}]),
wxMenu:connect(Panel, command_button_clicked, [{skip, true}]),
@@ -107,24 +131,47 @@ create_window(Notebook, ParentPid) ->
{border, 5}, {proportion,0}]),
wxWindow:setSizer(Panel, Sizer),
{Panel, #state{parent=ParentPid, panel=Panel,
- n_view=NodeView, p_view=ProcessView, m_view=ModView, f_view=FuncView,
+ n_view=NodeView, proc_view=ProcessView, port_view=PortView,
+ m_view=ModView, f_view=FuncView,
toggle_button = ToggleButton,
match_specs=default_matchspecs()}}.
default_matchspecs() ->
- Ms = [{"Return Trace", [{'_', [], [{return_trace}]}], "fun(_) -> return_trace() end"},
- {"Exception Trace", [{'_', [], [{exception_trace}]}], "fun(_) -> exception_trace() end"},
- {"Message Caller", [{'_', [], [{message,{caller}}]}], "fun(_) -> message(caller()) end"},
- {"Message Dump", [{'_', [], [{message,{process_dump}}]}], "fun(_) -> message(process_dump()) end"}],
+ [{Key,default_matchspecs(Key)} || Key <- [funcs,send,'receive']].
+default_matchspecs(Key) ->
+ Ms = get_default_matchspecs(Key),
[make_ms(Name,Term,FunStr) || {Name,Term,FunStr} <- Ms].
-create_process_view(Parent) ->
+get_default_matchspecs(funcs) ->
+ [{"Return Trace", [{'_', [], [{return_trace}]}],
+ "fun(_) -> return_trace() end"},
+ {"Exception Trace", [{'_', [], [{exception_trace}]}],
+ "fun(_) -> exception_trace() end"},
+ {"Message Caller", [{'_', [], [{message,{caller}}]}],
+ "fun(_) -> message(caller()) end"},
+ {"Message Dump", [{'_', [], [{message,{process_dump}}]}],
+ "fun(_) -> message(process_dump()) end"}];
+get_default_matchspecs(send) ->
+ [{"To local node", [{['$1','_'], [{'==',{node,'$1'},{node}}], []}],
+ "fun([Pid,_]) when node(Pid)==node() ->\n true\nend"},
+ {"To remote node", [{['$1','_'], [{'=/=',{node,'$1'},{node}}], []}],
+ "fun([Pid,_]) when node(Pid)=/=node() ->\n true\nend"}];
+get_default_matchspecs('receive') ->
+ [{"From local node", [{['$1','_','_'], [{'==','$1',{node}}], []}],
+ "fun([Node,_,_]) when Node==node() ->\n true\nend"},
+ {"From remote node", [{['$1','_','_'], [{'=/=','$1',{node}}], []}],
+ "fun([Node,_,_]) when Node=/=node() ->\n true\nend"}].
+
+
+create_proc_port_view(Parent) ->
Panel = wxPanel:new(Parent),
MainSz = wxBoxSizer:new(?wxHORIZONTAL),
Style = ?wxLC_REPORT bor ?wxLC_HRULES,
Splitter = wxSplitterWindow:new(Panel, [{style, ?SASH_STYLE}]),
Nodes = wxListCtrl:new(Splitter, [{winid, ?NODES_WIN}, {style, Style}]),
- Procs = wxListCtrl:new(Splitter, [{winid, ?PROC_WIN}, {style, Style}]),
+ ProcsPortsSplitter = wxSplitterWindow:new(Splitter, [{style, ?SASH_STYLE}]),
+ Procs = wxListCtrl:new(ProcsPortsSplitter, [{winid,?PROC_WIN},{style,Style}]),
+ Ports = wxListCtrl:new(ProcsPortsSplitter, [{winid,?PORT_WIN},{style,Style}]),
Li = wxListItem:new(),
wxListItem:setText(Li, "Nodes"),
wxListCtrl:insertColumn(Nodes, 0, Li),
@@ -136,31 +183,57 @@ create_process_view(Parent) ->
wxListCtrl:setColumnWidth(Procs, Col, DefSize),
Col + 1
end,
- ListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, 120},
- {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}],
- lists:foldl(AddProc, 0, ListItems),
+ ProcListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, 120},
+ {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}],
+ lists:foldl(AddProc, 0, ProcListItems),
+
+ AddPort = fun({Name, Align, DefSize}, Col) ->
+ wxListItem:setText(Li, Name),
+ wxListItem:setAlign(Li, Align),
+ wxListCtrl:insertColumn(Ports, Col, Li),
+ wxListCtrl:setColumnWidth(Ports, Col, DefSize),
+ Col + 1
+ end,
+ PortListItems = [{"Port Id", ?wxLIST_FORMAT_CENTER, 120},
+ {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}],
+ lists:foldl(AddPort, 0, PortListItems),
+
wxListItem:destroy(Li),
wxSplitterWindow:setSashGravity(Splitter, 0.0),
wxSplitterWindow:setMinimumPaneSize(Splitter,50),
- wxSplitterWindow:splitVertically(Splitter, Nodes, Procs, [{sashPosition, 155}]),
+ wxSplitterWindow:splitVertically(Splitter, Nodes, ProcsPortsSplitter,
+ [{sashPosition, 155}]),
wxSizer:add(MainSz, Splitter, [{flag, ?wxEXPAND}, {proportion, 1}]),
+ wxSplitterWindow:setSashGravity(ProcsPortsSplitter, 0.5),
+ wxSplitterWindow:setMinimumPaneSize(ProcsPortsSplitter,50),
+ wxSplitterWindow:splitHorizontally(ProcsPortsSplitter, Procs, Ports,
+ [{sashPosition, 182}]),
+
wxListCtrl:connect(Procs, command_list_item_right_click),
+ wxListCtrl:connect(Ports, command_list_item_right_click),
wxListCtrl:connect(Nodes, command_list_item_right_click),
+ wxListCtrl:connect(Nodes, command_list_item_selected),
wxListCtrl:connect(Procs, size, [{skip, true}]),
+ wxListCtrl:connect(Ports, size, [{skip, true}]),
wxListCtrl:connect(Nodes, size, [{skip, true}]),
+ wxListCtrl:setToolTip(Nodes, ?NO_NODES_HELP),
+ wxListCtrl:setToolTip(Procs, ?NO_P_HELP),
+ wxListCtrl:setToolTip(Ports, ?NO_P_HELP),
+
wxPanel:setSizer(Panel, MainSz),
wxWindow:setFocus(Procs),
- {Panel, Nodes, Procs}.
+ {Panel, Nodes, Procs, Ports}.
create_matchspec_view(Parent) ->
Panel = wxPanel:new(Parent),
MainSz = wxBoxSizer:new(?wxHORIZONTAL),
Style = ?wxLC_REPORT bor ?wxLC_HRULES,
Splitter = wxSplitterWindow:new(Panel, [{style, ?SASH_STYLE}]),
- Modules = wxListCtrl:new(Splitter, [{winid, ?MODULES_WIN}, {style, Style}]),
+ Modules = wxListCtrl:new(Splitter, [{winid, ?MODULES_WIN},
+ {style, Style bor ?wxLC_SINGLE_SEL}]),
Funcs = wxListCtrl:new(Splitter, [{winid, ?FUNCS_WIN}, {style, Style}]),
Li = wxListItem:new(),
@@ -182,7 +255,9 @@ create_matchspec_view(Parent) ->
wxListCtrl:connect(Modules, size, [{skip, true}]),
wxListCtrl:connect(Funcs, size, [{skip, true}]),
wxListCtrl:connect(Modules, command_list_item_selected),
+ wxListCtrl:connect(Modules, command_list_item_right_click),
wxListCtrl:connect(Funcs, command_list_item_right_click),
+ wxListCtrl:setToolTip(Panel, ?NO_TP_HELP),
wxPanel:setSizer(Panel, MainSz),
{Panel, Modules, Funcs}.
@@ -192,8 +267,11 @@ create_menues(Parent) ->
#create_menu{id = ?SAVE_TRACEOPTS, text = "Save settings"}]},
{"Options",
[#create_menu{id = ?TRACE_OUTPUT, text = "Output"},
- #create_menu{id = ?TRACE_DEFMS, text = "Match Specifications"},
- #create_menu{id = ?TRACE_DEFPS, text = "Default Process Options"}]}
+ #create_menu{id = ?DEF_MS_FUNCS, text = "Default Match Specifications for Functions"},
+ #create_menu{id = ?DEF_MS_SEND, text = "Default Match Specifications for 'send'"},
+ #create_menu{id = ?DEF_MS_RECV, text = "Default Match Specifications for 'receive'"},
+ #create_menu{id = ?DEF_PROC_OPTS, text = "Default Process Options"},
+ #create_menu{id = ?DEF_PORT_OPTS, text = "Default Port Options"}]}
],
observer_wx:create_menus(Parent, Menus).
@@ -206,11 +284,19 @@ handle_event(#wx{obj=Obj, event=#wxSize{size={W,_}}}, State) ->
end,
{noreply, State};
-handle_event(#wx{id=?ADD_NEW}, State = #state{panel=Parent, def_trace_opts=TraceOpts}) ->
+handle_event(#wx{id=?ADD_NEW_PROCS}, State = #state{panel=Parent, def_proc_flags=TraceOpts}) ->
try
Opts = observer_traceoptions_wx:process_trace(Parent, TraceOpts),
- Process = #tpid{pid=new, opts=Opts},
- {noreply, do_add_processes([Process], State#state{def_trace_opts=Opts})}
+ Process = #titem{id=new_processes, opts=Opts},
+ {noreply, do_add_processes([Process], State#state{def_proc_flags=Opts})}
+ catch cancel -> {noreply, State}
+ end;
+
+handle_event(#wx{id=?ADD_NEW_PORTS}, State = #state{panel=Parent, def_port_flags=TraceOpts}) ->
+ try
+ Opts = observer_traceoptions_wx:port_trace(Parent, TraceOpts),
+ Port = #titem{id=new_ports, opts=Opts},
+ {noreply, do_add_ports([Port], State#state{def_port_flags=Opts})}
catch cancel -> {noreply, State}
end;
@@ -233,26 +319,36 @@ handle_event(#wx{id=?MODULES_WIN, event=#wxList{type=command_list_item_selected,
update_functions_view(dict:fetch(Module, TPs), Fview),
{noreply, State};
+handle_event(#wx{id=?NODES_WIN,
+ event=#wxList{type=command_list_item_selected}},
+ State = #state{tpids=Tpids, tports=Tports, n_view=Nview,
+ proc_view=ProcView, port_view=PortView, nodes=Ns}) ->
+ Nodes = get_selected_items(Nview, Ns),
+ update_p_view(Tpids, ProcView, Nodes),
+ update_p_view(Tports, PortView, Nodes),
+ {noreply, State};
+
handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 1}},
#state{panel = Panel,
nodes = Nodes,
tpids = TProcs,
+ tports = TPorts,
tpatterns = TPs0,
toggle_button = ToggleBtn,
output = Opts
} = State) ->
try
TPs = dict:to_list(TPs0),
- (TProcs == []) andalso throw({error, "No processes traced"}),
+ (TProcs == []) andalso (TPorts == []) andalso throw({error, "No processes or ports traced"}),
(Nodes == []) andalso throw({error, "No nodes traced"}),
- HaveCallTrace = fun(#tpid{opts=Os}) -> lists:member(functions,Os) end,
+ HaveCallTrace = fun(#titem{opts=Os}) -> lists:member(functions,Os) end,
WStr = "Call trace actived but no trace patterns used",
(TPs == []) andalso lists:any(HaveCallTrace, TProcs) andalso
observer_wx:create_txt_dialog(Panel, WStr, "Warning", ?wxICON_WARNING),
{TTB, LogWin} = ttb_output_args(Panel, Opts),
{ok, _} = ttb:tracer(Nodes, TTB),
- setup_ttb(TPs, TProcs),
+ setup_ttb(TPs, TProcs, TPorts),
wxToggleButton:setLabel(ToggleBtn, "Stop Trace"),
{noreply, State#state{logwin=LogWin}}
catch {error, Msg} ->
@@ -302,7 +398,8 @@ handle_event(#wx{id=?LOG_SAVE, userData=TCtrl}, #state{panel=Panel} = State) ->
handle_event(#wx{id = ?SAVE_TRACEOPTS},
#state{panel = Panel,
- def_trace_opts = TraceOpts,
+ def_proc_flags = ProcFlags,
+ def_port_flags = PortFlags,
match_specs = MatchSpecs,
tpatterns = TracePatterns,
output = Output
@@ -312,7 +409,7 @@ handle_event(#wx{id = ?SAVE_TRACEOPTS},
?wxID_OK ->
Path = wxFileDialog:getPath(Dialog),
write_file(Panel, Path,
- TraceOpts, MatchSpecs, Output,
+ ProcFlags, PortFlags, MatchSpecs, Output,
dict:to_list(TracePatterns)
);
_ ->
@@ -333,52 +430,159 @@ handle_event(#wx{id = ?LOAD_TRACEOPTS}, #state{panel = Panel} = State) ->
wxDialog:destroy(Dialog),
{noreply, State2};
-handle_event(#wx{id=Type, event=#wxList{type=command_list_item_right_click}},
- State = #state{panel=Panel}) ->
- Menus = case Type of
- ?PROC_WIN ->
- [{?EDIT_PROCS, "Edit process options"},
- {?REMOVE_PROCS, "Remove processes"}];
- ?FUNCS_WIN ->
- [{?EDIT_FUNCS_MS, "Edit matchspecs"},
- {?REMOVE_FUNCS_MS, "Remove trace patterns"}];
- ?NODES_WIN ->
- [{?ADD_NODES, "Trace other nodes"},
- {?REMOVE_NODES, "Remove nodes"}]
- end,
- Menu = wxMenu:new(),
- [wxMenu:append(Menu,Id,Str) || {Id,Str} <- Menus],
- wxWindow:popupMenu(Panel, Menu),
- wxMenu:destroy(Menu),
+handle_event(#wx{id=?PROC_WIN, event=#wxList{type=command_list_item_right_click}},
+ State = #state{panel=Panel, proc_view=LCtrl, tpids=Tpids,
+ n_view=Nview, nodes=Nodes}) ->
+ case get_visible_ps(Tpids, Nodes, Nview) of
+ [] ->
+ ok;
+ Visible ->
+ case get_selected_items(LCtrl, Visible) of
+ [] ->
+ ok;
+ _ ->
+ create_right_click_menu(
+ Panel,
+ [{?EDIT_PROCS, "Edit process options"},
+ {?REMOVE_PROCS, "Remove processes"}])
+ end
+ end,
+ {noreply, State};
+
+handle_event(#wx{id=?PORT_WIN, event=#wxList{type=command_list_item_right_click}},
+ State = #state{panel=Panel, port_view=LCtrl, tports=Tports,
+ n_view=Nview, nodes=Nodes}) ->
+ case get_visible_ps(Tports, Nodes, Nview) of
+ [] ->
+ ok;
+ Visible ->
+ case get_selected_items(LCtrl, Visible) of
+ [] ->
+ ok;
+ _ ->
+ create_right_click_menu(
+ Panel,
+ [{?EDIT_PORTS, "Edit port options"},
+ {?REMOVE_PORTS, "Remove ports"}])
+ end
+ end,
+ {noreply, State};
+
+handle_event(#wx{id=?MODULES_WIN,event=#wxList{type=command_list_item_right_click}},
+ State = #state{panel=Panel, m_view=Mview, tpatterns=TPs}) ->
+ case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))) of
+ [] ->
+ ok;
+ _ ->
+ create_right_click_menu(
+ Panel,
+ [{?REMOVE_MOD_MS, "Remove trace patterns"}])
+ end,
+ {noreply,State};
+
+handle_event(#wx{id=?FUNCS_WIN,event=#wxList{type=command_list_item_right_click}},
+ State = #state{panel=Panel, m_view=Mview, f_view=Fview,
+ tpatterns=TPs}) ->
+ case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))) of
+ [] ->
+ ok;
+ [Module] ->
+ case get_selected_items(Fview, dict:fetch(Module, TPs)) of
+ [] ->
+ ok;
+ _ ->
+ create_right_click_menu(
+ Panel,
+ [{?EDIT_FUNCS_MS, "Edit matchspecs"},
+ {?REMOVE_FUNCS_MS, "Remove trace patterns"}])
+ end
+ end,
+ {noreply,State};
+
+handle_event(#wx{id=?NODES_WIN,event=#wxList{type=command_list_item_right_click}},
+ State = #state{panel=Panel, n_view=Nview, nodes=Nodes}) ->
+ Menu =
+ case get_selected_items(Nview, Nodes) of
+ [] ->
+ [{?ADD_NODES, "Add nodes"}];
+ _ ->
+ [{?ADD_NODES, "Add nodes"},
+ {?REMOVE_NODES, "Remove nodes"}]
+ end,
+ create_right_click_menu(Panel,Menu),
{noreply, State};
-handle_event(#wx{id=?EDIT_PROCS}, #state{panel=Panel, tpids=Tpids, p_view=Ps} = State) ->
+handle_event(#wx{id=?EDIT_PROCS}, #state{panel=Panel, tpids=Tpids, proc_view=Procs} = State) ->
try
- [#tpid{opts=DefOpts}|_] = Selected = get_selected_items(Ps, Tpids),
+ [#titem{opts=DefOpts}|_] = Selected = get_selected_items(Procs, Tpids),
Opts = observer_traceoptions_wx:process_trace(Panel, DefOpts),
- Changed = [Tpid#tpid{opts=Opts} || Tpid <- Selected],
- {noreply, do_add_processes(Changed, State#state{def_trace_opts=Opts})}
+ Changed = [Tpid#titem{opts=Opts} || Tpid <- Selected],
+ {noreply, do_add_processes(Changed, State#state{def_proc_flags=Opts})}
catch _:_ ->
{noreply, State}
end;
-handle_event(#wx{id=?REMOVE_PROCS}, #state{tpids=Tpids, p_view=LCtrl} = State) ->
+handle_event(#wx{id=?REMOVE_PROCS},
+ #state{tpids=Tpids, proc_view=LCtrl,
+ n_view=Nview, nodes=Nodes} = State) ->
Selected = get_selected_items(LCtrl, Tpids),
Pids = Tpids -- Selected,
- update_process_view(Pids, LCtrl),
+ update_p_view(Pids, LCtrl, Nodes, Nview),
{noreply, State#state{tpids=Pids}};
-handle_event(#wx{id=?TRACE_DEFPS}, #state{panel=Panel, def_trace_opts=PO} = State) ->
+handle_event(#wx{id=?EDIT_PORTS}, #state{panel=Panel, tports=Tports, port_view=Ports} = State) ->
+ try
+ [#titem{opts=DefOpts}|_] = Selected = get_selected_items(Ports, Tports),
+ Opts = observer_traceoptions_wx:port_trace(Panel, DefOpts),
+ Changed = [Tport#titem{opts=Opts} || Tport <- Selected],
+ {noreply, do_add_ports(Changed, State#state{def_port_flags=Opts})}
+ catch _:_ ->
+ {noreply, State}
+ end;
+
+handle_event(#wx{id=?REMOVE_PORTS},
+ #state{tports=Tports, port_view=LCtrl,
+ n_view=Nview, nodes=Nodes} = State) ->
+ Selected = get_selected_items(LCtrl, Tports),
+ Ports = Tports -- Selected,
+ update_p_view(Ports, LCtrl, Nodes, Nview),
+ {noreply, State#state{tports=Ports}};
+
+handle_event(#wx{id=?DEF_PROC_OPTS}, #state{panel=Panel, def_proc_flags=PO} = State) ->
try
Opts = observer_traceoptions_wx:process_trace(Panel, PO),
- {noreply, State#state{def_trace_opts=Opts}}
+ {noreply, State#state{def_proc_flags=Opts}}
catch _:_ ->
{noreply, State}
end;
-handle_event(#wx{id=?TRACE_DEFMS}, #state{panel=Panel, match_specs=Ms} = State) ->
+handle_event(#wx{id=?DEF_PORT_OPTS}, #state{panel=Panel, def_port_flags=PO} = State) ->
+ try
+ Opts = observer_traceoptions_wx:port_trace(Panel, PO),
+ {noreply, State#state{def_port_flags=Opts}}
+ catch _:_ ->
+ {noreply, State}
+ end;
+
+handle_event(#wx{id=?DEF_MS_FUNCS}, #state{panel=Panel, match_specs=Ms} = State) ->
try %% Return selected MS and sends new MS's to us
- observer_traceoptions_wx:select_matchspec(self(), Panel, Ms)
+ observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, funcs)
+ catch _:_ ->
+ cancel
+ end,
+ {noreply, State};
+
+handle_event(#wx{id=?DEF_MS_SEND}, #state{panel=Panel, match_specs=Ms} = State) ->
+ try %% Return selected MS and sends new MS's to us
+ observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, send)
+ catch _:_ ->
+ cancel
+ end,
+ {noreply, State};
+
+handle_event(#wx{id=?DEF_MS_RECV}, #state{panel=Panel, match_specs=Ms} = State) ->
+ try %% Return selected MS and sends new MS's to us
+ observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, 'receive')
catch _:_ ->
cancel
end,
@@ -389,12 +593,34 @@ handle_event(#wx{id=?EDIT_FUNCS_MS}, #state{panel=Panel, tpatterns=TPs,
match_specs=Mss
} = State) ->
try
- [Module] = get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))),
- Selected = get_selected_items(LCtrl, dict:fetch(Module, TPs)),
- Ms = observer_traceoptions_wx:select_matchspec(self(), Panel, Mss),
- Changed = [TP#tpattern{ms=Ms} || TP <- Selected],
- {noreply, do_add_patterns({Module, Changed}, State)}
- catch _:_ ->
+ case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))) of
+ [] ->
+ throw({error,"No module selected"});
+ [Module] ->
+ Selected = get_selected_items(LCtrl, dict:fetch(Module, TPs)),
+ Key = case Module of
+ 'Events' ->
+ SelectedEvents =
+ [Event || #tpattern{fa=Event} <- Selected],
+ E1 = hd(SelectedEvents),
+ case lists:all(fun(E) when E==E1 -> true;
+ (_) -> false
+ end,
+ SelectedEvents) of
+ true -> E1;
+ false -> throw({error,"Can not set match specs for multiple event types"})
+ end;
+ _ -> funcs
+ end,
+ Ms = observer_traceoptions_wx:select_matchspec(self(), Panel,
+ Mss, Key),
+ Changed = [TP#tpattern{ms=Ms} || TP <- Selected],
+ {noreply, do_add_patterns({Module, Changed}, State)}
+ end
+ catch {error, Msg} ->
+ observer_wx:create_txt_dialog(Panel, Msg, "Error", ?wxICON_ERROR),
+ {noreply, State};
+ cancel ->
{noreply, State}
end;
@@ -417,6 +643,16 @@ handle_event(#wx{id=?REMOVE_FUNCS_MS}, #state{tpatterns=TPs0, f_view=LCtrl, m_vi
{noreply, State#state{tpatterns=TPs}}
end;
+handle_event(#wx{id=?REMOVE_MOD_MS}, #state{tpatterns=TPs0, f_view=LCtrl, m_view=Mview} = State) ->
+ case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs0))) of
+ [] -> {noreply, State};
+ [Module] ->
+ update_functions_view([], LCtrl),
+ TPs = dict:erase(Module, TPs0),
+ update_modules_view(lists:sort(dict:fetch_keys(TPs)), Module, Mview),
+ {noreply, State#state{tpatterns=TPs}}
+ end;
+
handle_event(#wx{id=?TRACE_OUTPUT}, #state{panel=Panel, output=Out0} = State) ->
try
Out = observer_traceoptions_wx:output(Panel, Out0),
@@ -458,11 +694,20 @@ handle_call(Msg, From, _State) ->
error({unhandled_call, Msg, From}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-handle_cast({add_processes, Pids}, State = #state{panel=Parent, def_trace_opts=TraceOpts}) ->
+handle_cast({add_processes, Pids}, State = #state{panel=Parent, def_proc_flags=TraceOpts}) ->
try
Opts = observer_traceoptions_wx:process_trace(Parent, TraceOpts),
- POpts = [#tpid{pid=Pid, opts=Opts} || Pid <- Pids],
- S = do_add_processes(POpts, State#state{def_trace_opts=Opts}),
+ POpts = [#titem{id=Pid, opts=Opts} || Pid <- Pids],
+ S = do_add_processes(POpts, State#state{def_proc_flags=Opts}),
+ {noreply, S}
+ catch cancel ->
+ {noreply, State}
+ end;
+handle_cast({add_ports, Ports}, State = #state{panel=Parent, def_port_flags=TraceOpts}) ->
+ try
+ Opts = observer_traceoptions_wx:port_trace(Parent, TraceOpts),
+ POpts = [#titem{id=Id, opts=Opts} || Id <- Ports],
+ S = do_add_ports(POpts, State#state{def_port_flags=Opts}),
{noreply, S}
catch cancel ->
{noreply, State}
@@ -510,45 +755,93 @@ do_add_patterns({Module, NewPs}, State=#state{tpatterns=TPs0, m_view=Mview, f_vi
State#state{tpatterns=TPs}
end.
-do_add_processes(POpts, S0=#state{n_view=Nview, p_view=LCtrl, tpids=OldPids, nodes=Ns0}) ->
- case merge_pids(POpts, OldPids) of
- {OldPids, [], []} ->
- S0;
- {Pids, New, _Changed} ->
- update_process_view(Pids, LCtrl),
- Ns1 = lists:usort([node(Pid) || #tpid{pid=Pid} <- New, is_pid(Pid)]),
+do_add_processes(POpts, S0=#state{n_view=Nview, proc_view=LCtrl, tpids=OldPids, nodes=OldNodes}) ->
+ CheckFun = fun(Pid) -> is_pid(Pid) end,
+ {Pids, Nodes} = do_add_pid_or_port(POpts, Nview, LCtrl,
+ OldPids, OldNodes, CheckFun),
+ S0#state{tpids=Pids, nodes=Nodes}.
+
+do_add_ports(POpts, S0=#state{n_view=Nview, port_view=LCtrl, tports=OldPorts, nodes=OldNodes}) ->
+ CheckFun = fun(Port) -> is_port(Port) end,
+ {Ports, Nodes} = do_add_pid_or_port(POpts, Nview, LCtrl,
+ OldPorts, OldNodes, CheckFun),
+ S0#state{tports=Ports, nodes=Nodes}.
+
+do_add_pid_or_port(POpts, Nview, LCtrl, OldPs, Ns0, Check) ->
+ case merge_trace_items(POpts, OldPs) of
+ {OldPs, [], []} ->
+ {OldPs,Ns0};
+ {Ps, New, _Changed} ->
+ Ns1 = lists:usort([node(Id) || #titem{id=Id} <- New, Check(Id)]),
Nodes = case ordsets:subtract(Ns1, Ns0) of
+ [] when Ns0==[] -> [observer_wx:get_active_node()];
[] -> Ns0; %% No new Nodes
- NewNs ->
- %% if dynamicly updates add trace patterns for new nodes
- All = ordsets:union(NewNs, Ns0),
- update_nodes_view(All, Nview),
- All
+ NewNs -> ordsets:union(NewNs, Ns0)
end,
- S0#state{tpids=Pids, nodes=Nodes}
+ update_nodes_view(Nodes, Nview),
+ update_p_view(Ps, LCtrl, Nodes, Nview),
+ {Ps, Nodes}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-update_process_view(Pids, LCtrl) ->
+get_visible_ps(PidsOrPorts, [Node], _Nview) ->
+ %% If only one node, treat this as selected
+ get_visible_ps(PidsOrPorts, [Node]);
+get_visible_ps(PidsOrPorts, Nodes, Nview) ->
+ get_visible_ps(PidsOrPorts, get_selected_items(Nview, Nodes)).
+
+get_visible_ps(PidsOrPorts, Nodes) ->
+ %% Show pids/ports belonging to the selected nodes only (+ named pids/ports)
+ [P || P <- PidsOrPorts,
+ is_atom(P#titem.id) orelse
+ lists:member(node(P#titem.id),Nodes)].
+
+update_p_view(PidsOrPorts, LCtrl, Nodes, Nview) ->
+ update_p_view(get_visible_ps(PidsOrPorts, Nodes, Nview), LCtrl).
+update_p_view(PidsOrPorts, LCtrl, Nodes) ->
+ update_p_view(get_visible_ps(PidsOrPorts, Nodes), LCtrl).
+
+update_p_view(PidsOrPorts, LCtrl) ->
+ %% pid- or port-view
wxListCtrl:deleteAllItems(LCtrl),
- wx:foldl(fun(#tpid{pid=Pid, opts=Opts}, Row) ->
+ wx:foldl(fun(#titem{id=Id, opts=Opts}, Row) ->
_Item = wxListCtrl:insertItem(LCtrl, Row, ""),
?EVEN(Row) andalso
wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN),
- wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Pid)),
+ wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Id)),
wxListCtrl:setItem(LCtrl, Row, 1, observer_lib:to_str(Opts)),
Row+1
- end, 0, Pids).
+ end, 0, PidsOrPorts),
+ case PidsOrPorts of
+ [] ->
+ wxListCtrl:setToolTip(LCtrl,?NO_P_HELP);
+ _ ->
+ wxListCtrl:setToolTip(LCtrl,?P_HELP)
+ end.
update_nodes_view(Nodes, LCtrl) ->
+ Selected =
+ case Nodes of
+ [_] -> Nodes;
+ _ -> get_selected_items(LCtrl, Nodes)
+ end,
wxListCtrl:deleteAllItems(LCtrl),
wx:foldl(fun(Node, Row) ->
_Item = wxListCtrl:insertItem(LCtrl, Row, ""),
?EVEN(Row) andalso
wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN),
wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Node)),
+ lists:member(Node,Selected) andalso % keep selection
+ wxListCtrl:setItemState(LCtrl, Row, 16#FFFF,
+ ?wxLIST_STATE_SELECTED),
Row+1
- end, 0, Nodes).
+ end, 0, Nodes),
+ case Nodes of
+ [] ->
+ wxListCtrl:setToolTip(LCtrl,?NO_NODES_HELP);
+ _ ->
+ wxListCtrl:setToolTip(LCtrl,?NODES_HELP)
+ end.
update_modules_view(Mods, Module, LCtrl) ->
wxListCtrl:deleteAllItems(LCtrl),
@@ -560,33 +853,51 @@ update_modules_view(Mods, Module, LCtrl) ->
(Mod =:= Module) andalso
wxListCtrl:setItemState(LCtrl, Row, 16#FFFF, ?wxLIST_STATE_SELECTED),
Row+1
- end, 0, Mods).
+ end, 0, Mods),
+ Parent = wxListCtrl:getParent(LCtrl),
+ case Mods of
+ [] ->
+ wxListCtrl:setToolTip(Parent,?NO_TP_HELP);
+ _ ->
+ wxListCtrl:setToolTip(Parent,?TP_HELP)
+ end.
update_functions_view(Funcs, LCtrl) ->
wxListCtrl:deleteAllItems(LCtrl),
- wx:foldl(fun(#tpattern{fa=FA, ms=#match_spec{str=Ms}}, Row) ->
+ wx:foldl(fun(#tpattern{m=M, fa=FA, ms=#match_spec{str=Ms}}, Row) ->
_Item = wxListCtrl:insertItem(LCtrl, Row, ""),
?EVEN(Row) andalso wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN),
- wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str({func,FA})),
+ FuncStr =
+ case M of
+ 'Events' ->
+ observer_lib:to_str(FA);
+ _ ->
+ observer_lib:to_str({func,FA})
+ end,
+ wxListCtrl:setItem(LCtrl, Row, 0, FuncStr),
wxListCtrl:setItem(LCtrl, Row, 1, Ms),
Row+1
end, 0, Funcs).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-merge_pids([N1=#tpid{pid=new}|Ns], [N2=#tpid{pid=new}|Old]) ->
- {Pids, New, Changed} = merge_pids_1(Ns,Old),
- {[N1|Pids], New, [{N2,N2}|Changed]};
-merge_pids([N1=#tpid{pid=new}|Ns], Old) ->
- {Pids, New, Changed} = merge_pids_1(Ns,Old),
- {[N1|Pids], [N1|New], Changed};
-merge_pids(Ns, [N2=#tpid{pid=new}|Old]) ->
- {Pids, New, Changed} = merge_pids_1(Ns,Old),
- {[N2|Pids], New, Changed};
-merge_pids(New, Old) ->
- merge_pids_1(New, Old).
-
-merge_pids_1(New, Old) ->
- merge(lists:sort(New), Old, #tpid.pid, [], [], []).
+%% Trace items are processes and ports
+merge_trace_items([N1=#titem{id=NewP}|Ns], [N2=#titem{id=NewP}|Old])
+ when NewP==new_processes; NewP==new_ports ->
+ {Ids, New, Changed} = merge_trace_items_1(Ns,Old),
+ {[N1|Ids], New, [{N2,N2}|Changed]};
+merge_trace_items([N1=#titem{id=NewP}|Ns], Old)
+ when NewP==new_processes; NewP==new_ports ->
+ {Ids, New, Changed} = merge_trace_items_1(Ns,Old),
+ {[N1|Ids], [N1|New], Changed};
+merge_trace_items(Ns, [N2=#titem{id=NewP}|Old])
+ when NewP==new_processes; NewP==new_ports ->
+ {Ids, New, Changed} = merge_trace_items_1(Ns,Old),
+ {[N2|Ids], New, Changed};
+merge_trace_items(New, Old) ->
+ merge_trace_items_1(New, Old).
+
+merge_trace_items_1(New, Old) ->
+ merge(lists:sort(New), Old, #titem.id, [], [], []).
merge_patterns(New, Old) ->
merge(lists:sort(New), Old, #tpattern.fa, [], [], []).
@@ -676,10 +987,12 @@ create_logwindow(Parent, true) ->
wxFrame:show(LogWin),
{LogWin, Text}.
-setup_ttb(TPs, TPids) ->
+setup_ttb(TPs, TPids, TPorts) ->
_R1 = [setup_tps(FTP, []) || {_, FTP} <- TPs],
- _R2 = [ttb:p(Pid, dbg_flags(Flags)) || #tpid{pid=Pid, opts=Flags} <- TPids],
- [#tpid{pid=_Pid, opts=_Flags}|_] = TPids,
+ _R2 = [ttb:p(Pid, dbg_flags(proc,Flags)) ||
+ #titem{id=Pid, opts=Flags} <- TPids],
+ _R3 = [ttb:p(Port, dbg_flags(port,Flags)) ||
+ #titem{id=Port, opts=Flags} <- TPorts],
ok.
%% Sigh order is important
@@ -695,20 +1008,24 @@ setup_tps([First|Rest], Prev) ->
setup_tps([], Prev) ->
[setup_tp(TP) || TP <- lists:reverse(Prev)].
+setup_tp(#tpattern{m='Events',fa=Event, ms=#match_spec{term=Ms}}) ->
+ ttb:tpe(Event,Ms);
setup_tp(#tpattern{m=M,fa={F,A}, ms=#match_spec{term=Ms}}) ->
ttb:tpl(M,F,A,Ms).
-dbg_flags(Flags) ->
- [dbg_flag(Flag) || Flag <- Flags].
+dbg_flags(Type,Flags) ->
+ [dbg_flag(Type,Flag) || Flag <- Flags].
-dbg_flag(send) -> s;
-dbg_flag('receive') -> r;
-dbg_flag(functions) -> c;
-dbg_flag(on_spawn) -> sos;
-dbg_flag(on_link) -> sol;
-dbg_flag(on_first_spawn) -> sofs;
-dbg_flag(on_first_link) -> sofl;
-dbg_flag(events) -> p.
+dbg_flag(_,send) -> s;
+dbg_flag(_,'receive') -> r;
+dbg_flag(proc,functions) -> c;
+dbg_flag(proc,on_spawn) -> sos;
+dbg_flag(proc,on_link) -> sol;
+dbg_flag(proc,on_first_spawn) -> sofs;
+dbg_flag(proc,on_first_link) -> sofl;
+dbg_flag(proc,events) -> p;
+dbg_flag(port,events) -> ports;
+dbg_flag(_,Flag) -> Flag.
textformat(Trace) when element(1, Trace) == trace_ts, tuple_size(Trace) >= 4 ->
format_trace(Trace, tuple_size(Trace)-1, element(tuple_size(Trace),Trace));
@@ -784,23 +1101,28 @@ ftup(Trace, Index, Size) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-write_file(Frame, Filename, TraceOps, MatchSpecs, Output, TPs) ->
- FormatMS = fun(#match_spec{name=Id, term=T, func=F}) ->
- io_lib:format("[{name,\"~s\"}, {term, ~w}, {func, \"~s\"}]",
- [Id, T, F])
- end,
- FormatTP = fun({Module, FTPs}) ->
- List = format_ftp(FTPs, FormatMS),
- io_lib:format("{tp, ~w, [~s]}.~n",[Module, List])
+write_file(Frame, Filename, ProcFlags, PortFlags, MatchSpecs, Output, TPs) ->
+ MSToList = fun(#match_spec{name=Id, term=T, func=F}) ->
+ [{name,Id},{term,T},{func,F}]
end,
+ MSTermList = [{ms,Key,[MSToList(MS) || MS <- MSs]} ||
+ {Key,MSs} <- MatchSpecs],
+ TPToTuple = fun(#tpattern{fa={F,A}, ms=Ms}) ->
+ {F,A,MSToList(Ms)}
+ end,
+ ModuleTermList = [{tp, Module, [TPToTuple(FTP) || FTP <- FTPs]} ||
+ {Module,FTPs} <- TPs],
+
Str =
["%%%\n%%% This file is generated by Observer\n",
"%%%\n%%% DO NOT EDIT!\n%%%\n",
- [["{ms, ", FormatMS(Ms), "}.\n"] || Ms <- MatchSpecs],
- "{traceopts, ", io_lib:format("~w",[TraceOps]) ,"}.\n",
- "{output, ", io_lib:format("~w",[Output]) ,"}.\n",
- [FormatTP(TP) || TP <- TPs]
+ [io_lib:format("~p.~n",[MSTerm]) || MSTerm <- MSTermList],
+ io_lib:format("~p.~n",[{procflags,ProcFlags}]),
+ io_lib:format("~p.~n",[{portflags,PortFlags}]),
+ io_lib:format("~p.~n",[{output,Output}]),
+ [io_lib:format("~p.~n",[ModuleTerm]) || ModuleTerm <- ModuleTermList]
],
+
case file:write_file(Filename, list_to_binary(Str)) of
ok ->
success;
@@ -809,38 +1131,66 @@ write_file(Frame, Filename, TraceOps, MatchSpecs, Output, TPs) ->
observer_wx:create_txt_dialog(Frame, FailMsg, "Error", ?wxICON_ERROR)
end.
-format_ftp([#tpattern{fa={F,A}, ms=Ms}], FormatMS) ->
- io_lib:format("{~w, ~w, ~s}", [F,A,FormatMS(Ms)]);
-format_ftp([#tpattern{fa={F,A}, ms=Ms}|Rest], FormatMS) ->
- [io_lib:format("{~w, ~w, ~s},~n ", [F,A,FormatMS(Ms)]),
- format_ftp(Rest, FormatMS)].
-
-read_settings(Filename, #state{match_specs=Ms0, def_trace_opts=TO0} = State) ->
+read_settings(Filename, #state{match_specs=Ms0, def_proc_flags=ProcFs0, def_port_flags=PortFs0} = State) ->
case file:consult(Filename) of
{ok, Terms} ->
- Ms = lists:usort(Ms0 ++ [parse_ms(MsList) || {ms, MsList} <- Terms]),
- TOs = lists:usort(TO0 ++ proplists:get_value(traceopts, Terms, [])),
+ Ms = parse_ms(Terms, Ms0),
+ ProcFs1 = proplists:get_value(procflags, Terms, []) ++
+ proplists:get_value(traceopts, Terms, []), % for backwards comp.
+ ProcFs = lists:usort(ProcFs0 ++ ProcFs1),
+ PortFs = lists:usort(PortFs0 ++
+ proplists:get_value(portflags, Terms, [])),
Out = proplists:get_value(output, Terms, []),
lists:foldl(fun parse_tp/2,
- State#state{match_specs=Ms, def_trace_opts=TOs, output=Out},
+ State#state{match_specs=Ms, def_proc_flags=ProcFs,
+ def_port_flags=PortFs, output=Out},
Terms);
{error, _} ->
- observer_wx:create_txt_dialog(State#state.panel, "Could not load settings",
+ observer_wx:create_txt_dialog(State#state.panel,
+ "Could not load settings",
"Error", ?wxICON_ERROR),
State
end.
-parse_ms(Opts) ->
- Name = proplists:get_value(name, Opts, "TracePattern"),
- Term = proplists:get_value(term, Opts, [{'_',[],[ok]}]),
- FunStr = proplists:get_value(term, Opts, "fun(_) -> ok end"),
- make_ms(Name, Term, FunStr).
+parse_ms(Terms, OldMSs) ->
+ MSs =
+ case [{Key,[make_ms(MS) || MS <- MSs]} || {ms,Key,MSs} <- Terms] of
+ [] ->
+ case [make_ms(MS) || {ms,MS} <- Terms] of
+ [] ->
+ [];
+ FuncMSs -> % for backwards compatibility
+ [{funcs,FuncMSs}]
+ end;
+ KeyMSs ->
+ KeyMSs
+ end,
+ parse_ms_1(MSs, dict:from_list(OldMSs)).
+
+parse_ms_1([{Key,MSs} | T], Dict) ->
+ parse_ms_1(T, dict:append_list(Key,MSs,Dict));
+parse_ms_1([],Dict) ->
+ [{Key,rm_dups(MSs,[])} || {Key,MSs} <- dict:to_list(Dict)].
+
+rm_dups([H|T],Acc) ->
+ case lists:member(H,Acc) of
+ true ->
+ rm_dups(T,Acc);
+ false ->
+ rm_dups(T,[H|Acc])
+ end;
+rm_dups([],Acc) ->
+ lists:reverse(Acc).
+
+make_ms(MS) ->
+ [{func,FunStr},{name,Name},{term,Term}] = lists:keysort(1,MS),
+ make_ms(Name,Term,FunStr).
make_ms(Name, Term, FunStr) ->
#match_spec{name=Name, term=Term, str=io_lib:format("~w", Term), func = FunStr}.
parse_tp({tp, Mod, FAs}, State) ->
- Patterns = [#tpattern{m=Mod,fa={F,A}, ms=parse_ms(List)} ||
+ Patterns = [#tpattern{m=Mod,fa={F,A}, ms=make_ms(List)} ||
{F,A,List} <- FAs],
do_add_patterns({Mod, Patterns}, State);
parse_tp(_, State) ->
@@ -866,3 +1216,9 @@ get_indecies(Rest = [_|_], I, [_|T]) ->
get_indecies(Rest, I+1, T);
get_indecies(_, _, _) ->
[].
+
+create_right_click_menu(Panel,Menus) ->
+ Menu = wxMenu:new(),
+ [wxMenu:append(Menu,Id,Str) || {Id,Str} <- Menus],
+ wxWindow:popupMenu(Panel, Menu),
+ wxMenu:destroy(Menu).
diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl
index 9ba9b72b6f..285c298c4b 100644
--- a/lib/observer/src/observer_traceoptions_wx.erl
+++ b/lib/observer/src/observer_traceoptions_wx.erl
@@ -22,8 +22,8 @@
-include_lib("wx/include/wx.hrl").
-include("observer_defs.hrl").
--export([process_trace/2, trace_pattern/4, select_nodes/2,
- output/2, select_matchspec/3]).
+-export([process_trace/2, port_trace/2, trace_pattern/4, select_nodes/2,
+ output/2, select_matchspec/4]).
process_trace(Parent, Default) ->
Dialog = wxDialog:new(Parent, ?wxID_ANY, "Process Options",
@@ -36,12 +36,20 @@ process_trace(Parent, Default) ->
FuncBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace function call", []),
check_box(FuncBox, lists:member(functions, Default)),
+ ArityBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace arity instead of arguments", []),
+ check_box(ArityBox, lists:member(functions, Default)),
SendBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace send message", []),
check_box(SendBox, lists:member(send, Default)),
RecBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace receive message", []),
check_box(RecBox, lists:member('receive', Default)),
EventBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace process events", []),
check_box(EventBox, lists:member(events, Default)),
+ SchedBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace scheduling of processes", []),
+ check_box(SchedBox, lists:member(running_procs, Default)),
+ ExitBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace scheduling of exiting processes", []),
+ check_box(ExitBox, lists:member(exiting, Default)),
+ GCBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace garbage collections", []),
+ check_box(GCBox, lists:member(garbage_collection, Default)),
{SpawnBox, SpwnAllRadio, SpwnFirstRadio} =
optionpage_top_right(Panel, RightSz, [{flag, ?wxBOTTOM},{border, 5}], "spawn"),
@@ -57,7 +65,7 @@ process_trace(Parent, Default) ->
{Radio, Opt} <- [{SpwnAllRadio, on_spawn}, {SpwnFirstRadio, on_first_spawn},
{LinkAllRadio, on_link}, {LinkFirstRadio, on_first_link}]],
- [wxSizer:add(LeftSz, CheckBox, []) || CheckBox <- [FuncBox,SendBox,RecBox,EventBox]],
+ [wxSizer:add(LeftSz, CheckBox, []) || CheckBox <- [FuncBox,ArityBox,SendBox,RecBox,EventBox,SchedBox,ExitBox,GCBox]],
wxSizer:add(LeftSz, 150, -1),
wxSizer:add(PanelSz, LeftSz, [{flag, ?wxEXPAND}, {proportion,1}]),
@@ -80,7 +88,9 @@ process_trace(Parent, Default) ->
case wxDialog:showModal(Dialog) of
?wxID_OK ->
All = [{SendBox, send}, {RecBox, 'receive'},
- {FuncBox, functions}, {EventBox, events},
+ {FuncBox, functions}, {ArityBox, arity},
+ {EventBox, events}, {SchedBox, running_procs},
+ {ExitBox, exiting}, {GCBox, garbage_collection},
{{SpawnBox, SpwnAllRadio}, on_spawn},
{{SpawnBox,SpwnFirstRadio}, on_first_spawn},
{{LinkBox, LinkAllRadio}, on_link},
@@ -98,12 +108,57 @@ process_trace(Parent, Default) ->
throw(cancel)
end.
+port_trace(Parent, Default) ->
+ Dialog = wxDialog:new(Parent, ?wxID_ANY, "Port Options",
+ [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}]),
+ Panel = wxPanel:new(Dialog),
+ MainSz = wxBoxSizer:new(?wxVERTICAL),
+ OptsSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Tracing options"}]),
+
+ SendBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace send message", []),
+ check_box(SendBox, lists:member(send, Default)),
+ RecBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace receive message", []),
+ check_box(RecBox, lists:member('receive', Default)),
+ EventBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace port events", []),
+ check_box(EventBox, lists:member(events, Default)),
+ SchedBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace scheduling of ports", []),
+ check_box(SchedBox, lists:member(running_ports, Default)),
+
+ [wxSizer:add(OptsSz, CheckBox, []) || CheckBox <- [SendBox,RecBox,EventBox,SchedBox]],
+ wxSizer:add(OptsSz, 150, -1),
+
+ wxPanel:setSizer(Panel, OptsSz),
+ wxSizer:add(MainSz, Panel, [{flag, ?wxEXPAND}, {proportion,1}]),
+ Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL),
+ wxSizer:add(MainSz, Buttons, [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}]),
+ wxWindow:setSizerAndFit(Dialog, MainSz),
+ wxSizer:setSizeHints(MainSz, Dialog),
+
+ case wxDialog:showModal(Dialog) of
+ ?wxID_OK ->
+ All = [{SendBox, send}, {RecBox, 'receive'},
+ {EventBox, events}, {SchedBox, running_ports}],
+ Opts = [Id || {Tick, Id} <- All, wxCheckBox:getValue(Tick)],
+ wxDialog:destroy(Dialog),
+ lists:reverse(Opts);
+ ?wxID_CANCEL ->
+ wxDialog:destroy(Dialog),
+ throw(cancel)
+ end.
+
trace_pattern(ParentPid, Parent, Node, MatchSpecs) ->
try
- Module = module_selector(Parent, Node),
- MFAs = function_selector(Parent, Node, Module),
- MatchSpec = select_matchspec(ParentPid, Parent, MatchSpecs),
- {Module, [#tpattern{m=M,fa={F,A},ms=MatchSpec} || {M,F,A} <- MFAs]}
+ {Module,MFAs,MatchSpec} =
+ case module_selector(Parent, Node) of
+ {'$trace_event',Event} ->
+ MS = select_matchspec(ParentPid, Parent, MatchSpecs, Event),
+ {'Events',[{'Events',Event}],MS};
+ Mod ->
+ MFAs0 = function_selector(Parent, Node, Mod),
+ MS = select_matchspec(ParentPid, Parent, MatchSpecs, funcs),
+ {Mod,MFAs0,MS}
+ end,
+ {Module, [#tpattern{m=M,fa=FA,ms=MatchSpec} || {M,FA} <- MFAs]}
catch cancel -> cancel
end.
@@ -112,7 +167,7 @@ select_nodes(Parent, Nodes) ->
check_selector(Parent, Choices).
module_selector(Parent, Node) ->
- Dialog = wxDialog:new(Parent, ?wxID_ANY, "Select Module",
+ Dialog = wxDialog:new(Parent, ?wxID_ANY, "Select Module or Event",
[{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER},
{size, {400, 400}}]),
Panel = wxPanel:new(Dialog),
@@ -136,7 +191,9 @@ module_selector(Parent, Node) ->
wxWindow:setFocus(TxtCtrl),
%% init data
Modules = get_modules(Node),
- AllModules = [{atom_to_list(X), X} || X <- Modules],
+ Events = [{"Messages sent",{'$trace_event',send}},
+ {"Messages received",{'$trace_event','receive'}}],
+ AllModules = Events ++ [{atom_to_list(X), X} || X <- Modules],
filter_listbox_data("", AllModules, ListBox),
wxTextCtrl:connect(TxtCtrl, command_text_updated,
[{callback, fun(#wx{event=#wxCommand{cmdString=Input}}, _) ->
@@ -174,9 +231,9 @@ function_selector(Parent, Node, Module) ->
not(erl_internal:guard_bif(Name, Arity))]),
ParsedChoices = parse_function_names(Choices),
case check_selector(Parent, ParsedChoices) of
- [] -> [{Module, '_', '_'}];
+ [] -> [{Module, {'_', '_'}}];
FAs ->
- [{Module, F, A} || {F,A} <- FAs]
+ [{Module, {F, A}} || {F,A} <- FAs]
end.
check_selector(Parent, ParsedChoices) ->
@@ -268,7 +325,12 @@ get_checked(ListBox, Acc) ->
lists:reverse(Acc)
end.
-select_matchspec(Pid, Parent, MatchSpecs) ->
+select_matchspec(Pid, Parent, AllMatchSpecs, Key) ->
+ {MatchSpecs,RestMS} =
+ case lists:keytake(Key,1,AllMatchSpecs) of
+ {value,{Key,MSs0},Rest} -> {MSs0,Rest};
+ false -> {[],AllMatchSpecs}
+ end,
Dialog = wxDialog:new(Parent, ?wxID_ANY, "Trace Match Specifications",
[{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER},
{size, {400, 400}}]),
@@ -313,8 +375,12 @@ select_matchspec(Pid, Parent, MatchSpecs) ->
filter_listbox_data("", Choices, ListBox),
Add = fun(_,_) ->
- case edit_ms(TextCtrl, new, Parent) of
- Ms = #match_spec{} -> add_and_select(-1, Ms, ListBox);
+ case edit_ms(TextCtrl, new, Dialog) of
+ Ms = #match_spec{} ->
+ add_and_select(-1, Ms, ListBox),
+ wxWindow:enable(OkButt),
+ wxWindow:enable(EditMsBtn),
+ wxWindow:enable(DelMsBtn);
Else -> Else
end
end,
@@ -323,8 +389,12 @@ select_matchspec(Pid, Parent, MatchSpecs) ->
case SelId >= 0 of
true ->
#match_spec{name=Name} = wxListBox:getClientData(ListBox,SelId),
- case edit_ms(TextCtrl, Name, Parent) of
- Ms = #match_spec{} -> add_and_select(SelId, Ms, ListBox);
+ case edit_ms(TextCtrl, Name, Dialog) of
+ Ms = #match_spec{} ->
+ add_and_select(SelId, Ms, ListBox),
+ wxWindow:enable(OkButt),
+ wxWindow:enable(EditMsBtn),
+ wxWindow:enable(DelMsBtn);
Else -> Else
end;
false ->
@@ -367,7 +437,7 @@ select_matchspec(Pid, Parent, MatchSpecs) ->
Count = wxListBox:getCount(ListBox),
MSs = [wxListBox:getClientData(ListBox, Id) ||
Id <- lists:seq(0, Count-1)],
- Pid ! {update_ms, MSs},
+ Pid ! {update_ms, [{Key,MSs}|RestMS]},
MS = lists:nth(SelId+1, MSs),
wxDialog:destroy(Dialog),
MS;
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index 1860f2f469..59f6443551 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -37,7 +37,8 @@
-define(ID_UNREADABLE, 405).
-define(ID_SYSTEM_TABLES, 406).
-define(ID_TABLE_INFO, 407).
-
+-define(ID_SHOW_TABLE, 408).
+
-record(opt, {type=ets,
sys_hidden=true,
unread_hidden=true,
@@ -49,6 +50,7 @@
{
parent,
grid,
+ panel,
node=node(),
opt=#opt{},
selected,
@@ -86,12 +88,13 @@ init([Notebook, Parent]) ->
wxListItem:destroy(Li),
wxListCtrl:connect(Grid, command_list_item_activated),
+ wxListCtrl:connect(Grid, command_list_item_right_click),
wxListCtrl:connect(Grid, command_list_item_selected),
wxListCtrl:connect(Grid, command_list_col_click),
wxListCtrl:connect(Grid, size, [{skip, true}]),
wxWindow:setFocus(Grid),
- {Panel, #state{grid=Grid, parent=Parent, timer={false, 10}}}.
+ {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer={false, 10}}}.
handle_event(#wx{id=?ID_REFRESH},
State = #state{node=Node, grid=Grid, opt=Opt}) ->
@@ -145,6 +148,16 @@ handle_event(#wx{event=#wxList{type=command_list_item_activated,
end,
{noreply, State};
+handle_event(#wx{event=#wxList{type=command_list_item_right_click}},
+ State=#state{panel=Panel}) ->
+
+ Menu = wxMenu:new(),
+ wxMenu:append(Menu, ?ID_TABLE_INFO, "Table info"),
+ wxMenu:append(Menu, ?ID_SHOW_TABLE, "Show Table Content"),
+ wxWindow:popupMenu(Panel, Menu),
+ wxMenu:destroy(Menu),
+ {noreply, State};
+
handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Index}},
State) ->
{noreply, State#state{selected=Index}};
@@ -160,6 +173,22 @@ handle_event(#wx{id=?ID_TABLE_INFO},
{noreply, State}
end;
+handle_event(#wx{id=?ID_SHOW_TABLE},
+ State=#state{grid=Grid, node=Node, opt=#opt{type=Type}, tabs=Tabs, selected=Sel}) ->
+ case Sel of
+ undefined ->
+ {noreply, State};
+ R when is_integer(R) ->
+ Table = lists:nth(Sel+1, Tabs),
+ case Table#tab.protection of
+ private ->
+ self() ! {error, "Table has 'private' protection and can not be read"};
+ _ ->
+ observer_tv_table:start_link(Grid, [{node,Node}, {type,Type}, {table,Table}])
+ end,
+ {noreply, State}
+ end;
+
handle_event(#wx{id=?ID_REFRESH_INTERVAL},
State = #state{grid=Grid, timer=Timer0}) ->
Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60),
@@ -315,6 +344,7 @@ display_table_info(Parent0, Node, Source, Table) ->
{_, Sizer, _} = observer_lib:display_info(Frame, [IdInfo,Settings,Memory]),
wxSizer:setSizeHints(Sizer, Frame),
+ wxWindow:setMinSize(Frame, {300, -1}),
wxFrame:center(Frame),
wxFrame:show(Frame).
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 30bd4043e4..301bb4b32f 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -21,8 +21,8 @@
-behaviour(wx_object).
-export([start/0, stop/0]).
--export([create_menus/2, get_attrib/1, get_tracer/0, set_status/1,
- create_txt_dialog/4, try_rpc/4, return_to_localnode/2]).
+-export([create_menus/2, get_attrib/1, get_tracer/0, get_active_node/0,
+ set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]).
-export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3,
handle_call/3, handle_info/2, check_page_title/1]).
@@ -55,6 +55,7 @@
notebook,
main_panel,
pro_panel,
+ port_panel,
tv_panel,
sys_panel,
trace_panel,
@@ -90,6 +91,9 @@ set_status(What) ->
get_tracer() ->
wx_object:call(observer, get_tracer).
+get_active_node() ->
+ wx_object:call(observer, get_active_node).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init(_Args) ->
@@ -165,6 +169,10 @@ setup(#state{frame = Frame} = State) ->
ProPanel = observer_pro_wx:start_link(Notebook, self()),
wxNotebook:addPage(Notebook, ProPanel, "Processes", []),
+ %% Port Panel
+ PortPanel = observer_port_wx:start_link(Notebook, self()),
+ wxNotebook:addPage(Notebook, PortPanel, "Ports", []),
+
%% Table Viewer Panel
TVPanel = observer_tv_wx:start_link(Notebook, self()),
wxNotebook:addPage(Notebook, TVPanel, "Table Viewer", []),
@@ -188,6 +196,7 @@ setup(#state{frame = Frame} = State) ->
status_bar = StatusBar,
sys_panel = SysPanel,
pro_panel = ProPanel,
+ port_panel = PortPanel,
tv_panel = TVPanel,
trace_panel = TracePanel,
app_panel = AppPanel,
@@ -379,6 +388,9 @@ handle_call({get_attrib, Attrib}, _From, State) ->
handle_call(get_tracer, _From, State=#state{trace_panel=TraceP}) ->
{reply, TraceP, State};
+handle_call(get_active_node, _From, State=#state{node=Node}) ->
+ {reply, Node, State};
+
handle_call(stop, From, State) ->
stop_servers(State),
{noreply, State#state{reply_to=From}};
@@ -406,16 +418,21 @@ handle_info({nodedown, Node},
create_txt_dialog(Frame, Msg, "Node down", ?wxICON_EXCLAMATION),
{noreply, State3};
-handle_info({open_link, Pid0}, State = #state{pro_panel=ProcViewer, frame=Frame}) ->
- Pid = case Pid0 of
- [_|_] -> try list_to_pid(Pid0) catch _:_ -> Pid0 end;
- _ -> Pid0
+handle_info({open_link, Id0}, State = #state{pro_panel=ProcViewer,
+ port_panel=PortViewer,
+ frame=Frame}) ->
+ Id = case Id0 of
+ [_|_] -> try list_to_pid(Id0) catch _:_ -> Id0 end;
+ _ -> Id0
end,
%% Forward to process tab
- case is_pid(Pid) of
- true -> wx_object:get_pid(ProcViewer) ! {procinfo_open, Pid};
- false ->
- Msg = io_lib:format("Information about ~p is not available or implemented",[Pid]),
+ case Id of
+ Pid when is_pid(Pid) ->
+ wx_object:get_pid(ProcViewer) ! {procinfo_open, Pid};
+ "#Port" ++ _ = Port ->
+ wx_object:get_pid(PortViewer) ! {portinfo_open, Port};
+ _ ->
+ Msg = io_lib:format("Information about ~p is not available or implemented",[Id]),
Info = wxMessageDialog:new(Frame, Msg),
wxMessageDialog:showModal(Info),
wxMessageDialog:destroy(Info)
@@ -541,10 +558,11 @@ check_page_title(Notebook) ->
get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys,
tv_panel=Tv, trace_panel=Trace, app_panel=App,
- perf_panel=Perf, allc_panel=Alloc
+ perf_panel=Perf, allc_panel=Alloc, port_panel=Port
}) ->
Panel = case check_page_title(Notebook) of
"Processes" -> Pro;
+ "Ports" -> Port;
"System" -> Sys;
"Table Viewer" -> Tv;
?TRACE_STR -> Trace;
diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl
index 4d6eb3ba8d..ac6c4572eb 100644
--- a/lib/observer/src/ttb.erl
+++ b/lib/observer/src/ttb.erl
@@ -25,7 +25,8 @@
-export([tracer/0,tracer/1,tracer/2,p/2,stop/0,stop/1,start_trace/4]).
-export([get_et_handler/0]).
-export([tp/2, tp/3, tp/4, ctp/0, ctp/1, ctp/2, ctp/3, tpl/2, tpl/3, tpl/4,
- ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3]).
+ ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3,
+ tpe/2, ctpe/1]).
-export([seq_trigger_ms/0,seq_trigger_ms/1]).
-export([write_trace_info/2]).
-export([write_config/2,write_config/3,run_config/1,run_config/2,list_config/1]).
@@ -397,16 +398,16 @@ arg_list([A1|A],Acc) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Set trace flags on processes
-p(Procs0,Flags0) ->
+p(ProcsPorts0,Flags0) ->
ensure_no_overloaded_nodes(),
- store(p,[Procs0,Flags0]),
- no_store_p(Procs0,Flags0).
-no_store_p(Procs0,Flags0) ->
+ store(p,[ProcsPorts0,Flags0]),
+ no_store_p(ProcsPorts0,Flags0).
+no_store_p(ProcsPorts0,Flags0) ->
case transform_flags(to_list(Flags0)) of
{error,Reason} ->
{error,Reason};
Flags ->
- Procs = procs(Procs0),
+ ProcsPorts = procs_ports(ProcsPorts0),
case lists:foldl(fun(P,{PMatched,Ps}) -> case dbg:p(P,Flags) of
{ok,Matched} ->
{[{P,Matched}|PMatched],[P|Ps]};
@@ -414,7 +415,7 @@ no_store_p(Procs0,Flags0) ->
display_warning(P,Reason),
{PMatched,Ps}
end
- end,{[],[]},Procs) of
+ end,{[],[]},ProcsPorts) of
{[],[]} -> {error, no_match};
{SuccMatched,Succ} ->
no_store_write_trace_info(flags,{Succ,Flags}),
@@ -429,20 +430,22 @@ transform_flags(Flags) ->
dbg:transform_flags([timestamp | Flags]).
-procs(Procs) when is_list(Procs) ->
- lists:foldl(fun(P,Acc) -> proc(P)++Acc end,[],Procs);
-procs(Proc) ->
- proc(Proc).
+procs_ports(Procs) when is_list(Procs) ->
+ lists:foldl(fun(P,Acc) -> proc_port(P)++Acc end,[],Procs);
+procs_ports(Proc) ->
+ proc_port(Proc).
-proc(Procs) when Procs=:=all; Procs=:=ports; Procs=:=processes;
- Procs=:=existing; Procs=:=existing_ports; Procs=:=existing_processes;
- Procs=:=new; Procs=:=new_ports; Procs=:=new_processes ->
- [Procs];
-proc(Name) when is_atom(Name) ->
+proc_port(P) when P=:=all; P=:=ports; P=:=processes;
+ P=:=existing; P=:=existing_ports; P=:=existing_processes;
+ P=:=new; P=:=new_ports; P=:=new_processes ->
+ [P];
+proc_port(Name) when is_atom(Name) ->
[Name]; % can be registered on this node or other node
-proc(Pid) when is_pid(Pid) ->
+proc_port(Pid) when is_pid(Pid) ->
[Pid];
-proc({global,Name}) ->
+proc_port(Port) when is_port(Port) ->
+ [Port];
+proc_port({global,Name}) ->
case global:whereis_name(Name) of
Pid when is_pid(Pid) ->
[Pid];
@@ -479,6 +482,11 @@ tpl(A,B,C,D) ->
store(tpl,[A,B,C,ms(D)]),
dbg:tpl(A,B,C,ms(D)).
+tpe(A,B) ->
+ ensure_no_overloaded_nodes(),
+ store(tpe,[A,ms(B)]),
+ dbg:tpe(A,ms(B)).
+
ctp() ->
store(ctp,[]),
dbg:ctp().
@@ -518,6 +526,10 @@ ctpg(A,B,C) ->
store(ctpg,[A,B,C]),
dbg:ctpg(A,B,C).
+ctpe(A) ->
+ store(ctpe,[A]),
+ dbg:ctpe(A).
+
ms(return) ->
[{'_',[],[{return_trace}]}];
ms(caller) ->
@@ -1298,6 +1310,9 @@ ip_to_file(Trace, {shell_only, Fun} = State) ->
ip_to_file(Trace,{{file,File}, ShellOutput}) ->
Fun = dbg:trace_port(file,File), %File can be a filename or a wrap spec
Port = Fun(),
+ %% Just in case this is on the traced node,
+ %% make sure the port is not traced.
+ p(Port,clear),
%% Store the port so it can be properly closed
?MODULE ! {ip_to_file_trace_port, Port, self()},
receive {?MODULE,ok} -> ok end,
diff --git a/lib/observer/test/observer_SUITE.erl b/lib/observer/test/observer_SUITE.erl
index ae8111dcd6..4c882ad951 100644
--- a/lib/observer/test/observer_SUITE.erl
+++ b/lib/observer/test/observer_SUITE.erl
@@ -171,6 +171,7 @@ test_page("Applications" ++ _, _Window) ->
test_page("Processes" ++ _, _Window) ->
timer:sleep(500), %% Give it time to refresh
Active = get_active(),
+ Active ! refresh_interval,
ChangeSort = fun(N) ->
FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
Active ! FakeEv,
@@ -184,7 +185,23 @@ test_page("Processes" ++ _, _Window) ->
timer:sleep(1000), %% Give it time to refresh
ok;
-test_page(_Title = "Table" ++ _, _Window) ->
+test_page("Ports" ++ _, _Window) ->
+ timer:sleep(500), %% Give it time to refresh
+ Active = get_active(),
+ Active ! refresh_interval,
+ ChangeSort = fun(N) ->
+ FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
+ Active ! FakeEv,
+ timer:sleep(200)
+ end,
+ [ChangeSort(N) || N <- lists:seq(1,4) ++ [0]],
+ Activate = #wx{event=#wxList{type=command_list_item_activated,
+ itemIndex=2}},
+ Active ! Activate,
+ timer:sleep(1000), %% Give it time to refresh
+ ok;
+
+test_page("Table" ++ _, _Window) ->
Tables = [ets:new(list_to_atom("Test-" ++ [C]), [public]) || C <- lists:seq($A, $Z)],
Table = lists:nth(3, Tables),
ets:insert(Table, [{N,100-N} || N <- lists:seq(1,100)]),
@@ -208,6 +225,13 @@ test_page(_Title = "Table" ++ _, _Window) ->
timer:sleep(1000),
ok;
+test_page("Trace Overview" ++ _, _Window) ->
+ timer:sleep(500), %% Give it time to refresh
+ Active = get_active(),
+ Active ! refresh_interval,
+ timer:sleep(1000), %% Give it time to refresh
+ ok;
+
test_page(Title, Window) ->
io:format("Page ~p: ~p~n", [Title, Window]),
%% Just let it display some info and hopefully it doesn't crash
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index 8cdb5a43e3..c0d4665bda 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -1155,7 +1155,7 @@ all() ->
[send,'receive',call,procs,ports,garbage_collection,running,
set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link,
timestamp,monotonic_timestamp,strict_monotonic_timestamp,
- arity,return_to,silent,running_procs,running_ports].
+ arity,return_to,silent,running_procs,running_ports,exiting].
display_info([Node|Nodes]) ->
io:format("~nNode ~w:~n",[Node]),
@@ -1313,6 +1313,9 @@ tc_loop(Other, _Handler, _HData) ->
gen_reader(ip, {Host, Portno}) ->
case gen_tcp:connect(Host, Portno, [{active, false}, binary]) of
{ok, Sock} ->
+ %% Just in case this is on the traced node,
+ %% make sure the port is not traced.
+ p(Sock,clear),
mk_reader(fun ip_read/2, Sock);
Error ->
exit(Error)
diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl
index 66653c5b7f..cedb677178 100644
--- a/lib/runtime_tools/src/observer_backend.erl
+++ b/lib/runtime_tools/src/observer_backend.erl
@@ -23,7 +23,8 @@
-export([vsn/0]).
%% observer stuff
--export([sys_info/0, get_table/3, get_table_list/2, fetch_stats/2]).
+-export([sys_info/0, get_port_list/0,
+ get_table/3, get_table_list/2, fetch_stats/2]).
%% etop stuff
-export([etop_collect/1]).
@@ -139,6 +140,15 @@ get_mnesia_loop(Parent, {Match, Cont}) ->
Parent ! {self(), Match},
get_mnesia_loop(Parent, mnesia:select(Cont)).
+get_port_list() ->
+ [begin
+ [{port_id,P}|erlang:port_info(P)] ++
+ case erlang:port_info(P,monitors) of
+ undefined -> [];
+ Monitors -> [Monitors]
+ end
+ end || P <- erlang:ports()].
+
get_table_list(ets, Opts) ->
HideUnread = proplists:get_value(unread_hidden, Opts, true),
HideSys = proplists:get_value(sys_hidden, Opts, true),
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index db80d4c9e3..562f040477 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -50,7 +50,15 @@
-define(Empint(X), (ssh_bits:mpint(X))/binary ).
-define(Ebinary(X), ?STRING(X) ).
--define(unicode_list(B), unicode:characters_to_list(B)).
+ucl(B) ->
+ try unicode:characters_to_list(B) of
+ L when is_list(L) -> L;
+ {error,_Matched,Rest} -> throw({error,{bad_unicode,Rest}})
+ catch
+ _:_ -> throw({error,bad_unicode})
+ end.
+
+-define(unicode_list(B), ucl(B)).
encode(#ssh_msg_global_request{
name = Name,
diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl
index 9910b8f1d7..6894f83547 100644
--- a/lib/ssh/test/ssh_algorithms_SUITE.erl
+++ b/lib/ssh/test/ssh_algorithms_SUITE.erl
@@ -28,7 +28,7 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--define(TIMEOUT, 10000).
+-define(TIMEOUT, 35000).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -70,10 +70,10 @@ two_way_tags() -> [cipher,mac,compression].
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- ct:log("os:getenv(\"HOME\") = ~p~n"
- "init:get_argument(home) = ~p",
- [os:getenv("HOME"), init:get_argument(home)]),
- ct:log("~n~n"
+ ct:log("~n"
+ "Environment:~n============~n"
+ "os:getenv(\"HOME\") = ~p~n"
+ "init:get_argument(home) = ~p~n~n~n"
"OS ssh:~n=======~n~p~n~n~n"
"Erl ssh:~n========~n~p~n~n~n"
"Installed ssh client:~n=====================~n~p~n~n~n"
@@ -82,7 +82,9 @@ init_per_suite(Config) ->
" -- Default dh group exchange parameters ({min,def,max}): ~p~n"
" -- dh_default_groups: ~p~n"
" -- Max num algorithms: ~p~n"
- ,[os:cmd("ssh -V"),
+ ,[os:getenv("HOME"),
+ init:get_argument(home),
+ os:cmd("ssh -V"),
ssh:default_algorithms(),
ssh_test_lib:default_algorithms(sshc),
ssh_test_lib:default_algorithms(sshd),
@@ -136,14 +138,14 @@ end_per_group(_Alg, Config) ->
-init_per_testcase(sshc_simple_exec, Config) ->
+init_per_testcase(sshc_simple_exec_os_cmd, Config) ->
start_pubkey_daemon([?config(pref_algs,Config)], Config);
init_per_testcase(_TC, Config) ->
Config.
-end_per_testcase(sshc_simple_exec, Config) ->
+end_per_testcase(sshc_simple_exec_os_cmd, Config) ->
case ?config(srvr_pid,Config) of
Pid when is_pid(Pid) ->
ssh:stop_daemon(Pid),
@@ -154,7 +156,6 @@ end_per_testcase(sshc_simple_exec, Config) ->
end_per_testcase(_TC, Config) ->
Config.
-
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
@@ -221,18 +222,36 @@ interpolate(Is) ->
%%--------------------------------------------------------------------
%% Use the ssh client of the OS to connect
-sshc_simple_exec(Config) ->
+sshc_simple_exec_os_cmd(Config) ->
PrivDir = ?config(priv_dir, Config),
KnownHosts = filename:join(PrivDir, "known_hosts"),
{Host,Port} = ?config(srvr_addr, Config),
- Cmd = lists:concat(["ssh -p ",Port,
- " -C",
- " -o UserKnownHostsFile=",KnownHosts,
- " -o StrictHostKeyChecking=no",
- " ",Host," 1+1."]),
- ct:log("~p",[Cmd]),
- OpenSsh = ssh_test_lib:open_port({spawn, Cmd}, [eof,exit_status]),
- ssh_test_lib:rcv_expected({data,<<"2\n">>}, OpenSsh, ?TIMEOUT).
+ Parent = self(),
+ Client = spawn(
+ fun() ->
+ Cmd = lists:concat(["ssh -p ",Port,
+ " -C"
+ " -o UserKnownHostsFile=",KnownHosts,
+ " -o StrictHostKeyChecking=no"
+ " ",Host," 1+1."]),
+ Result = os:cmd(Cmd),
+ ct:log("~p~n = ~p",[Cmd, Result]),
+ Parent ! {result, self(), Result, "2"}
+ end),
+ receive
+ {result, Client, RawResult, Expect} ->
+ Lines = string:tokens(RawResult, "\r\n"),
+ case lists:any(fun(Line) -> Line==Expect end,
+ Lines) of
+ true ->
+ ok;
+ false ->
+ ct:log("Bad result: ~p~nExpected: ~p~nMangled result: ~p", [RawResult,Expect,Lines]),
+ {fail, "Bad result"}
+ end
+ after ?TIMEOUT ->
+ ct:fail("Did not receive answer")
+ end.
%%--------------------------------------------------------------------
%% Connect to the ssh server of the OS
@@ -299,7 +318,7 @@ specific_test_cases(Tag, Alg, SshcAlgos, SshdAlgos) ->
true ->
case ssh_test_lib:ssh_type() of
openSSH ->
- [sshc_simple_exec];
+ [sshc_simple_exec_os_cmd];
_ ->
[]
end;
diff --git a/lib/ssh/test/ssh_benchmark_SUITE.erl b/lib/ssh/test/ssh_benchmark_SUITE.erl
index d9be1a32b7..5d8c94be73 100644
--- a/lib/ssh/test/ssh_benchmark_SUITE.erl
+++ b/lib/ssh/test/ssh_benchmark_SUITE.erl
@@ -184,7 +184,7 @@ openssh_client_shell(Config, Options) ->
end, Times),
ssh:stop_daemon(ServerPid),
ok
- after 10000 ->
+ after 60*1000 ->
ssh:stop_daemon(ServerPid),
exit(SlavePid, kill),
{fail, timeout}
@@ -215,6 +215,7 @@ openssh_client_sftp(Config, Options) ->
{root, SftpSrcDir}])]},
{failfun, fun ssh_test_lib:failfun/2}
| Options]),
+ ct:pal("ServerPid = ~p",[ServerPid]),
ct:sleep(500),
Cmd = lists:concat(["sftp",
" -b -",
@@ -231,7 +232,7 @@ openssh_client_sftp(Config, Options) ->
end),
receive
{SlavePid, _ClientResponse} ->
- ct:pal("ClientResponse = ~p",[_ClientResponse]),
+ ct:pal("ClientResponse = ~p~nServerPid = ~p",[_ClientResponse,ServerPid]),
{ok, List} = get_trace_list(TracerPid),
%%ct:pal("List=~p",[List]),
Times = find_times(List, [channel_open_close]),
@@ -260,7 +261,7 @@ openssh_client_sftp(Config, Options) ->
end, Times),
ssh:stop_daemon(ServerPid),
ok
- after 10000 ->
+ after 2*60*1000 ->
ssh:stop_daemon(ServerPid),
exit(SlavePid, kill),
{fail, timeout}
@@ -445,10 +446,18 @@ increment({Alg,Sz,T},[]) ->
%%% API for the traceing
%%%
get_trace_list(TracerPid) ->
+ MonRef = monitor(process, TracerPid),
TracerPid ! {get_trace_list,self()},
receive
- {trace_list,L} -> {ok, pair_events(lists:reverse(L))}
- after 5000 -> {error,no_reply}
+ {trace_list,L} ->
+ demonitor(MonRef),
+ {ok, pair_events(lists:reverse(L))};
+ {'DOWN', MonRef, process, TracerPid, Info} ->
+ {error, {tracer_down,Info}}
+
+ after 3*60*1000 ->
+ demonitor(MonRef),
+ {error,no_reply}
end.
erlang_trace() ->
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index f6d7be41d6..26fe0935e1 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -1,4 +1,4 @@
-%%
+%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
@@ -93,35 +93,35 @@ groups() ->
init_per_group(not_unicode, Config) ->
ct:comment("Begin ~p",[grps(Config)]),
DataDir = ?config(data_dir, Config),
- PrivDir = ?config(priv_dir, Config),
[{user, "Alladin"},
{passwd, "Sesame"},
{data, <<"Hello world!">>},
- {filename, filename:join(PrivDir, "sftp.txt")},
- {testfile, filename:join(PrivDir, "test.txt")},
- {linktest, filename:join(PrivDir, "link_test.txt")},
- {tar_filename, filename:join(PrivDir, "sftp_tar_test.tar")},
- {tar_F1_txt, "f1.txt"},
+ {filename, "sftp.txt"},
+ {testfile, "test.txt"},
+ {linktest, "link_test.txt"},
+ {tar_filename, "sftp_tar_test.tar"},
+ {tar_F1_txt, "f1.txt"},
{datadir_tar, filename:join(DataDir,"sftp_tar_test_data")}
| Config];
init_per_group(unicode, Config) ->
- case file:native_name_encoding() of
- utf8 ->
+ case (file:native_name_encoding() == utf8)
+ andalso ("四" == [22235])
+ of
+ true ->
ct:comment("Begin ~p",[grps(Config)]),
DataDir = ?config(data_dir, Config),
- PrivDir = ?config(priv_dir, Config),
NewConfig =
[{user, "åke高兴"},
{passwd, "ärlig日本じん"},
{data, <<"foobar å 一二三四いちにさんち">>},
- {filename, filename:join(PrivDir, "sftp瑞点.txt")},
- {testfile, filename:join(PrivDir, "testハンス.txt")},
- {linktest, filename:join(PrivDir, "link_test語.txt")},
- {tar_filename, filename:join(PrivDir, "sftp_tar_test一二三.tar")},
- {tar_F1_txt, "F一.txt"},
- {tar_F3_txt, "f3.txt"},
- {tar_F4_txt, "g四.txt"},
+ {filename, "sftp瑞点.txt"},
+ {testfile, "testハンス.txt"},
+ {linktest, "link_test語.txt"},
+ {tar_filename, "sftp_tar_test一二三.tar"},
+ {tar_F1_txt, "F一.txt"},
+ {tar_F3_txt, "f3.txt"},
+ {tar_F4_txt, "g四.txt"},
{datadir_tar, filename:join(DataDir,"sftp_tar_test_data_高兴")}
| lists:foldl(fun(K,Cf) -> lists:keydelete(K,1,Cf) end,
Config,
@@ -228,8 +228,8 @@ init_per_testcase(sftp_nonexistent_subsystem, Config) ->
]),
[{sftpd, Sftpd} | Config];
-init_per_testcase(version_option, Config) ->
- prep(Config),
+init_per_testcase(version_option, Config0) ->
+ Config = prepare(Config0),
TmpConfig0 = lists:keydelete(watchdog, 1, Config),
TmpConfig = lists:keydelete(sftp, 1, TmpConfig0),
Dog = ct:timetrap(?default_timeout),
@@ -246,8 +246,8 @@ init_per_testcase(version_option, Config) ->
Sftp = {ChannelPid, Connection},
[{sftp,Sftp}, {watchdog, Dog} | TmpConfig];
-init_per_testcase(Case, Config0) ->
- prep(Config0),
+init_per_testcase(Case, Config00) ->
+ Config0 = prepare(Config00),
Config1 = lists:keydelete(watchdog, 1, Config0),
Config2 = lists:keydelete(sftp, 1, Config1),
Dog = ct:timetrap(2 * ?default_timeout),
@@ -279,7 +279,7 @@ init_per_testcase(Case, Config0) ->
[{sftp, Sftp}, {watchdog, Dog} | Config2]
end,
- case catch ?config(remote_tar,Config) of
+ case catch proplists:get_value(remote_tar,Config) of
%% The 'catch' is for the case of Config={skip,...}
true ->
%% Provide a ChannelPid independent of the sftp-channel already opened.
@@ -329,7 +329,7 @@ open_close_file(Server, File, Mode) ->
open_close_dir() ->
[{doc, "Test API functions opendir/2 and close/2"}].
open_close_dir(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = ?config(sftp_priv_dir, Config),
{Sftp, _} = ?config(sftp, Config),
FileName = ?config(filename, Config),
@@ -351,7 +351,7 @@ read_file(Config) when is_list(Config) ->
read_dir() ->
[{doc,"Test API function list_dir/2"}].
read_dir(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = ?config(sftp_priv_dir, Config),
{Sftp, _} = ?config(sftp, Config),
{ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir),
ct:log("sftp list dir: ~p~n", [Files]).
@@ -415,7 +415,7 @@ sftp_read_big_file(Config) when is_list(Config) ->
remove_file() ->
[{doc,"Test API function delete/2"}].
remove_file(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = ?config(sftp_priv_dir, Config),
FileName = ?config(filename, Config),
{Sftp, _} = ?config(sftp, Config),
@@ -429,7 +429,7 @@ remove_file(Config) when is_list(Config) ->
rename_file() ->
[{doc, "Test API function rename_file/2"}].
rename_file(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = ?config(sftp_priv_dir, Config),
FileName = ?config(filename, Config),
NewFileName = ?config(testfile, Config),
@@ -449,7 +449,7 @@ rename_file(Config) when is_list(Config) ->
mk_rm_dir() ->
[{doc,"Test API functions make_dir/2, del_dir/2"}].
mk_rm_dir(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = ?config(sftp_priv_dir, Config),
{Sftp, _} = ?config(sftp, Config),
DirName = filename:join(PrivDir, "test"),
@@ -945,7 +945,7 @@ aes_ctr_stream_crypto_tar(Config) ->
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
-prep(Config) ->
+oldprep(Config) ->
DataDir = ?config(data_dir, Config),
TestFile = ?config(filename, Config),
TestFile1 = ?config(testfile, Config),
@@ -965,6 +965,36 @@ prep(Config) ->
ok = file:write_file_info(TestFile,
FileInfo#file_info{mode = Mode}).
+prepare(Config0) ->
+ PrivDir = proplists:get_value(priv_dir, Config0),
+ Dir = filename:join(PrivDir, random_chars(10)),
+ file:make_dir(Dir),
+ Keys = [filename,
+ testfile,
+ linktest,
+ tar_filename],
+ Config1 = foldl_keydelete(Keys, Config0),
+ Config2 = lists:foldl(fun({Key,Name}, ConfAcc) ->
+ [{Key, filename:join(Dir,Name)} | ConfAcc]
+ end,
+ Config1,
+ lists:zip(Keys, [proplists:get_value(K,Config0) || K<-Keys])),
+
+ DataDir = proplists:get_value(data_dir, Config2),
+ FilenameSrc = filename:join(DataDir, "sftp.txt"),
+ FilenameDst = proplists:get_value(filename, Config2),
+ {ok,_} = file:copy(FilenameSrc, FilenameDst),
+ [{sftp_priv_dir,Dir} | Config2].
+
+
+random_chars(N) -> [crypto:rand_uniform($a,$z) || _<-lists:duplicate(N,x)].
+
+foldl_keydelete(Keys, L) ->
+ lists:foldl(fun(K,E) -> lists:keydelete(K,1,E) end,
+ L,
+ Keys).
+
+
chk_tar(Items, Config) ->
chk_tar(Items, Config, []).
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index c6541461a1..a1291146e4 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -655,17 +655,48 @@ sshc(Tag) ->
).
ssh_type() ->
- case os:find_executable("ssh") of
- false -> not_found;
- _ ->
- case os:cmd("ssh -V") of
- "OpenSSH" ++ _ ->
- openSSH;
- Str ->
- ct:log("ssh client ~p is unknown",[Str]),
- unknown
- end
- end.
+ Parent = self(),
+ Pid = spawn(fun() ->
+ Parent ! {ssh_type,self(),ssh_type1()}
+ end),
+ MonitorRef = monitor(process, Pid),
+ receive
+ {ssh_type, Pid, Result} ->
+ demonitor(MonitorRef),
+ Result;
+ {'DOWN', MonitorRef, process, Pid, _Info} ->
+ ct:log("~p:~p Process DOWN",[?MODULE,?LINE]),
+ not_found
+ after
+ 10000 ->
+ ct:log("~p:~p Timeout",[?MODULE,?LINE]),
+ demonitor(MonitorRef),
+ not_found
+ end.
+
+
+ssh_type1() ->
+ try
+ case os:find_executable("ssh") of
+ false ->
+ ct:log("~p:~p Executable \"ssh\" not found",[?MODULE,?LINE]),
+ not_found;
+ _ ->
+ case os:cmd("ssh -V") of
+ "OpenSSH" ++ _ ->
+ openSSH;
+ Str ->
+ ct:log("ssh client ~p is unknown",[Str]),
+ unknown
+ end
+ end
+ catch
+ Class:Exception ->
+ ct:log("~p:~p Exception ~p:~p",[?MODULE,?LINE,Class,Exception]),
+ not_found
+ end.
+
+
algo_intersection([], _) -> [];
algo_intersection(_, []) -> [];
diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index 3609b8d88e..016302fe50 100644
--- a/system/doc/efficiency_guide/advanced.xml
+++ b/system/doc/efficiency_guide/advanced.xml
@@ -35,8 +35,7 @@
how much memory different data types and operations require. It is
implementation-dependent how much memory the Erlang data types and
other items consume, but the following table shows some figures for
- the <c>erts-5.2</c> system in R9B. There have been no significant
- changes in R13.</p>
+ the <c>erts-8.0</c> system in OTP 19.0.</p>
<p>The unit of measurement is memory words. There exists both a
32-bit and a 64-bit implementation. A word is therefore 4 bytes or
@@ -87,6 +86,19 @@
<cell>2 words + the size of each element.</cell>
</row>
<row>
+ <cell>Small Map</cell>
+ <cell>4 words + 2 words per entry (key and value) + the size of each key and value pair.</cell>
+ </row>
+ <row>
+ <cell>Large Map</cell>
+ <cell>
+ At least, 2 words + 2 x <c>N</c> words + 2 x log16(<c>N</c>) words +
+ the size of each key and value pair, where <c>N</c> is the number of pairs in the Map.
+ A large Map is represented as a tree internally where each node in the tree is a
+ "sparse tuple" of arity 16.
+ </cell>
+ </row>
+ <row>
<cell>Pid</cell>
<cell>1 word for a process identifier from the current local node
+ 5 words for a process identifier from another node.<br></br>
@@ -122,7 +134,7 @@
</row>
<row>
<cell>Erlang process</cell>
- <cell>327 words when spawned, including a heap of 233 words.</cell>
+ <cell>338 words when spawned, including a heap of 233 words.</cell>
</row>
<tcaption>Memory Size of Different Data Types</tcaption>
</table>