aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
authorSverker Eriksson <[email protected]>2016-03-03 19:34:50 +0100
committerSverker Eriksson <[email protected]>2016-05-04 19:53:36 +0200
commit4a4475dea3ab0daf29f9a49ce845fa062387362a (patch)
tree00bd0d6579ae1c3990fd6f8170ba4ce41a3746b8 /erts/emulator
parentaf2a4c5a40c62da775caa92df5164fbee08b9245 (diff)
downloadotp-4a4475dea3ab0daf29f9a49ce845fa062387362a.tar.gz
otp-4a4475dea3ab0daf29f9a49ce845fa062387362a.tar.bz2
otp-4a4475dea3ab0daf29f9a49ce845fa062387362a.zip
erts: Add match spec for send trace
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/beam/beam_bp.c5
-rw-r--r--erts/emulator/beam/erl_bif_trace.c57
-rw-r--r--erts/emulator/beam/erl_trace.c59
-rw-r--r--erts/emulator/beam/erl_trace.h8
-rw-r--r--erts/emulator/test/trace_SUITE.erl123
5 files changed, 220 insertions, 32 deletions
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
index c4b9259d64..1e30e8d8d1 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -248,7 +248,10 @@ erts_bp_match_export(BpFunctions* f, Eterm mfa[3], int specified)
void
erts_bp_free_matched_functions(BpFunctions* f)
{
- Free(f->matching);
+ if (f->matching) {
+ Free(f->matching);
+ }
+ else ASSERT(f->matched == 0);
}
void
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index ef01aa3340..6b5bb2f889 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -68,6 +68,9 @@ static struct { /* Protected by code write permission */
static Eterm
trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist);
+static void
+erts_set_trace_send_pattern(Process*, Binary*, int on);
+
#ifdef ERTS_SMP
static void smp_bp_finisher(void* arg);
#endif
@@ -85,14 +88,23 @@ static void install_exp_breakpoints(BpFunctions* f);
static void uninstall_exp_breakpoints(BpFunctions* f);
static void clean_export_entries(BpFunctions* f);
+ErtsTracingEvent erts_send_tracing[ERTS_NUM_BP_IX];
+
void
erts_bif_trace_init(void)
{
+ int i;
+
erts_default_trace_pattern_is_on = 0;
erts_default_match_spec = NULL;
erts_default_meta_match_spec = NULL;
erts_default_trace_pattern_flags = erts_trace_pattern_flags_off;
erts_default_meta_tracer = erts_tracer_nil;
+
+ for (i=0; i<ERTS_NUM_BP_IX; i++) {
+ erts_send_tracing[i].on = 1;
+ erts_send_tracing[i].match_spec = NULL;
+ }
}
/*
@@ -314,8 +326,15 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
matches = erts_set_trace_pattern(p, mfa, specified,
match_prog_set, match_prog_set,
on, flags, meta_tracer, 0);
+ } else if (MFA == am_send) {
+ if (is_global || flags.breakpoint || on > ERTS_BREAK_SET) {
+ goto error;
+ }
+ erts_set_trace_send_pattern(p, match_prog_set, on);
+ matches = 1;
}
+
error:
MatchSetUnref(match_prog_set);
UnUseTmpHeap(3,p);
@@ -1467,6 +1486,43 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
return matches;
}
+void
+erts_set_trace_send_pattern(Process*p, Binary* match_spec, int on)
+{
+ ErtsTracingEvent* st = &erts_send_tracing[erts_staging_bp_ix()];
+
+ MatchSetUnref(st->match_spec);
+
+ st->on = on;
+ st->match_spec = match_spec;
+ MatchSetRef(match_spec);
+
+ finish_bp.current = 1; /* prepare phase not needed for send trace */
+ finish_bp.install = on;
+ finish_bp.e.matched = 0;
+ finish_bp.e.matching = NULL;
+ finish_bp.f.matched = 0;
+ finish_bp.f.matching = NULL;
+
+#ifndef ERTS_SMP
+ while (erts_finish_breakpointing()) {
+ /* Empty loop body */
+ }
+#endif
+}
+
+static void
+consolidate_send_tracing(void)
+{
+ ErtsTracingEvent* src = &erts_send_tracing[erts_active_bp_ix()];
+ ErtsTracingEvent* dst = &erts_send_tracing[erts_staging_bp_ix()];
+
+ MatchSetUnref(dst->match_spec);
+ dst->on = src->on;
+ dst->match_spec = src->match_spec;
+ MatchSetRef(dst->match_spec);
+}
+
int
erts_finish_breakpointing(void)
{
@@ -1542,6 +1598,7 @@ erts_finish_breakpointing(void)
erts_consolidate_bp_data(&finish_bp.f, 1);
erts_bp_free_matched_functions(&finish_bp.e);
erts_bp_free_matched_functions(&finish_bp.f);
+ consolidate_send_tracing();
return 0;
default:
ASSERT(0);
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 56899f574a..e8516cd09c 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -394,7 +394,8 @@ static ERTS_INLINE int
send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p,
Eterm t_p_id, ErtsTracerNif *tnif,
enum ErtsTracerOpt topt,
- Eterm tag, Eterm msg, Eterm extra);
+ Eterm tag, Eterm msg, Eterm extra,
+ Eterm pam_result);
static ERTS_INLINE Eterm
call_enabled_tracer(Process *c_p, const ErtsTracer tracer,
ErtsTracerNif **tnif_ref,
@@ -786,7 +787,7 @@ trace_sched_aux(Process *p, ErtsProcLocks locks, Eterm what)
}
send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SCHED_PROC,
- what, tmp, THE_NON_VALUE);
+ what, tmp, THE_NON_VALUE, am_true);
}
/* Send {trace_ts, Pid, What, {Mod, Func, Arity}, Timestamp}
@@ -811,9 +812,31 @@ trace_send(Process *p, Eterm to, Eterm msg)
{
Eterm operation = am_send;
ErtsTracerNif *tnif = NULL;
+ ErtsTracingEvent* te;
+ Eterm pam_result;
ASSERT(ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND));
+ te = &erts_send_tracing[erts_active_bp_ix()];
+ if (!te->on) {
+ return;
+ }
+ if (te->match_spec) {
+ Eterm args[2];
+ Uint32 return_flags;
+ args[0] = to;
+ args[1] = msg;
+ pam_result = erts_match_set_run(p, te->match_spec, args, 2,
+ ERTS_PAM_TMP_RESULT, &return_flags);
+ if (is_non_value(pam_result)
+ || pam_result == am_false
+ || (ERTS_TRACE_FLAGS(p) & F_TRACE_SILENT)) {
+ erts_match_set_release_result(p);
+ return;
+ }
+ } else
+ pam_result = am_true;
+
if (is_internal_pid(to)) {
if (!erts_proc_lookup(to))
goto send_to_non_existing_process;
@@ -825,9 +848,11 @@ trace_send(Process *p, Eterm to, Eterm msg)
}
if (is_tracer_enabled(p, ERTS_PROC_LOCK_MAIN, &p->common, &tnif,
- TRACE_FUN_E_SEND, operation))
+ TRACE_FUN_E_SEND, operation)) {
send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SEND,
- operation, msg, to);
+ operation, msg, to, pam_result);
+ }
+ erts_match_set_release_result(p);
}
/* Send {trace_ts, Pid, receive, Msg, Timestamp}
@@ -841,7 +866,7 @@ trace_receive(Process *c_p, Eterm msg)
TRACE_FUN_E_RECEIVE, am_receive))
send_to_tracer_nif(NULL, &c_p->common, c_p->common.id,
tnif, TRACE_FUN_T_RECEIVE,
- am_receive, msg, THE_NON_VALUE);
+ am_receive, msg, THE_NON_VALUE, am_true);
}
int
@@ -963,7 +988,7 @@ erts_trace_return_to(Process *p, BeamInstr *pc)
}
send_to_tracer_nif(p, &p->common, p->common.id, NULL, TRACE_FUN_T_CALL,
- am_return_to, mfa, THE_NON_VALUE);
+ am_return_to, mfa, THE_NON_VALUE, am_true);
}
@@ -1289,7 +1314,7 @@ trace_proc(Process *c_p, ErtsProcLocks c_p_locks,
if (is_tracer_enabled(c_p, c_p_locks, &t_p->common, &tnif,
TRACE_FUN_E_PROCS, what))
send_to_tracer_nif(c_p, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_PROCS,
- what, data, THE_NON_VALUE);
+ what, data, THE_NON_VALUE, am_true);
}
@@ -1315,7 +1340,7 @@ trace_proc_spawn(Process *p, Eterm what, Eterm pid,
mfa = TUPLE3(hp, mod, func, args);
hp += 4;
send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_PROCS,
- what, pid, mfa);
+ what, pid, mfa, am_true);
}
}
@@ -1365,7 +1390,7 @@ trace_gc(Process *p, Eterm what, Uint size)
msg = CONS(hp, tup, msg); hp += 2;
send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_GC,
- what, msg, am_undefined);
+ what, msg, am_undefined, am_true);
}
}
@@ -1780,7 +1805,7 @@ trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) {
ERTS_SMP_CHK_NO_PROC_LOCKS;
if (is_tracer_enabled(NULL, 0, &p->common, &tnif, TRACE_FUN_E_PORTS, am_open))
send_to_tracer_nif(NULL, &p->common, p->common.id, tnif, TRACE_FUN_T_PORTS,
- am_open, calling_pid, drv_name);
+ am_open, calling_pid, drv_name, am_true);
}
/* Sends trace message:
@@ -1799,7 +1824,7 @@ trace_port(Port *t_p, Eterm what, Eterm data) {
ERTS_SMP_CHK_NO_PROC_LOCKS;
if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_PORTS, what))
send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_PORTS,
- what, data, THE_NON_VALUE);
+ what, data, THE_NON_VALUE, am_true);
}
@@ -1936,7 +1961,7 @@ trace_port_receive(Port *t_p, Eterm caller, Eterm what, ...)
data = TUPLE2(hp, caller, data);
send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_RECEIVE,
- am_receive, data, THE_NON_VALUE);
+ am_receive, data, THE_NON_VALUE, am_true);
if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0)
erts_bin_free(bptr);
@@ -1959,7 +1984,7 @@ trace_port_send(Port *t_p, Eterm receiver, Eterm msg, int exists)
ERTS_SMP_CHK_NO_PROC_LOCKS;
if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SEND, op))
send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_SEND,
- op, msg, receiver);
+ op, msg, receiver, am_true);
}
void trace_port_send_binary(Port *t_p, Eterm to, Eterm what, char *bin, Sint sz)
@@ -1988,7 +2013,7 @@ void trace_port_send_binary(Port *t_p, Eterm to, Eterm what, char *bin, Sint sz)
hp += 3;
send_to_tracer_nif(NULL, &t_p->common, t_p->common.id, tnif, TRACE_FUN_T_SEND,
- am_send, msg, to);
+ am_send, msg, to, am_true);
if (bptr && erts_refc_dectest(&bptr->refc, 1) == 0)
erts_bin_free(bptr);
@@ -2019,7 +2044,7 @@ trace_sched_ports_where(Port *t_p, Eterm what, Eterm where) {
if (is_tracer_enabled(NULL, 0, &t_p->common, &tnif, TRACE_FUN_E_SCHED_PORT, what))
send_to_tracer_nif(NULL, &t_p->common, t_p->common.id,
tnif, TRACE_FUN_T_SCHED_PORT,
- what, where, THE_NON_VALUE);
+ what, where, THE_NON_VALUE, am_true);
}
/* Port profiling */
@@ -2842,7 +2867,7 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee,
static ERTS_INLINE int
send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p,
Eterm t_p_id, ErtsTracerNif *tnif, enum ErtsTracerOpt topt,
- Eterm tag, Eterm msg, Eterm extra)
+ Eterm tag, Eterm msg, Eterm extra, Eterm pam_result)
{
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
if (c_p) {
@@ -2862,7 +2887,7 @@ send_to_tracer_nif(Process *c_p, ErtsPTabElementCommon *t_p,
is_internal_pid(t_p->id) ? (Process*)t_p : NULL,
t_p->tracer, t_p->trace_flags,
t_p_id, tnif, topt, tag, msg, extra,
- am_true);
+ pam_result);
}
static ERTS_INLINE Eterm
diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h
index 9a007e62ec..e8e968c0cf 100644
--- a/erts/emulator/beam/erl_trace.h
+++ b/erts/emulator/beam/erl_trace.h
@@ -220,4 +220,12 @@ ERTS_DECLARE_DUMMY(erts_tracer_nil) = NIL;
&& erts_is_tracer_proc_enabled(PROC, ERTS_PROC_LOCK_MAIN, \
&(PROC)->common, am_trace_status))
+typedef struct
+{
+ int on;
+ struct binary* match_spec;
+} ErtsTracingEvent;
+
+extern ErtsTracingEvent erts_send_tracing[];
+
#endif /* ERL_TRACE_H__ */
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index b7f312635e..c8cc0fc6e8 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -255,30 +255,111 @@ send_trace(Config) when is_list(Config) ->
%% Check that a message sent to another process is traced.
1 = erlang:trace(Sender, true, [send]),
+ F1 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, Receiver, to_receiver},
+ {trace, Sender, send, to_receiver, Receiver} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F1, [no,
+ [{[Receiver,to_receiver],[],[]}],
+ [{['_','_'],[],[]}],
+ [{['$1','_'],[{is_pid,'$1'}],[]}],
+ [{['_','$1'],[{is_atom,'$1'}],[]}],
+ true]),
+
+ %% Test {message, Msg}
+ F1m = fun ({Pat, Msg}) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, Receiver, to_receiver},
+ {trace, Sender, send, to_receiver, Receiver, Msg} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F1m, [{[{['_','_'],[],[{message, 4711}]}], 4711},
+ {[{['_','_'],[],[{message, "4711"}]}], "4711"}
+ ]),
+
+ %% Test {message, {process_dump}}
+ set_trace_pattern(send, [{['_','_'],[],[{message, {process_dump}}]}], []),
Sender ! {send_please, Receiver, to_receiver},
- {trace, Sender, send, to_receiver, Receiver} = receive_first_trace(),
+ {trace, Sender, send, to_receiver, Receiver, ProcDump} = receive_first_trace(),
+ true = is_binary(ProcDump),
receive_nothing(),
+ %% Same test with false match spec
+ F2 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, Receiver, to_receiver},
+ receive_nothing()
+ end,
+ lists:foreach(F2, [[{[Sender,to_receiver],[],[]}],
+ [{[Receiver,nomatch],[],[]}],
+ [{['$1','_'],[{is_atom,'$1'}],[]}],
+ [{['_','$1'],[{is_pid,'$1'}],[]}],
+ false,
+ [{['_','_'],[],[{message,false}]}],
+ [{['_','_'],[],[{silent,true}]}]]),
+ erlang:trace_pattern(send, true, []),
+ erlang:trace(Sender, false, [silent]),
+
%% Check that a message sent to another registered process is traced.
register(?MODULE,Receiver),
- Sender ! {send_please, ?MODULE, to_receiver},
- {trace, Sender, send, to_receiver, ?MODULE} = receive_first_trace(),
- receive_nothing(),
+ F3 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, ?MODULE, to_receiver},
+ {trace, Sender, send, to_receiver, ?MODULE} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F3, [no,
+ [{[?MODULE,to_receiver],[],[]}],
+ [{['_','_'],[],[]}],
+ [{['$1','_'],[{is_atom,'$1'}],[]}],
+ [{['_','$1'],[{is_atom,'$1'}],[]}],
+ true]),
+ %% Again with false match spec
+ F4 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, ?MODULE, to_receiver},
+ receive_nothing()
+ end,
+ lists:foreach(F4, [[{[nomatch,to_receiver],[],[]}],
+ [{[?MODULE,nomatch],[],[]}],
+ [{['$1','_'],[{is_pid,'$1'}],[]}],
+ [{['_','$1'],[{is_pid,'$1'}],[]}],
+ [{['_','_'],[],[{message,false}]}],
+ [{['_','_'],[],[{silent,true}]}]
+ ]),
unregister(?MODULE),
+ erlang:trace_pattern(send, true, []),
+ erlang:trace(Sender, false, [silent]),
%% Check that a message sent to this process is traced.
- Sender ! {send_please, self(), to_myself},
- receive to_myself -> ok end,
- Self = self(),
- {trace, Sender, send, to_myself, Self} = receive_first_trace(),
- receive_nothing(),
+ F5 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, self(), to_myself},
+ receive to_myself -> ok end,
+ Self = self(),
+ {trace, Sender, send, to_myself, Self} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F5, [no,
+ [{[self(),to_myself],[],[]}],
+ [{['_','_'],[],[]}],
+ true]),
%% Check that a message sent to dead process is traced.
{Pid,Ref} = spawn_monitor(fun() -> ok end),
receive {'DOWN',Ref,_,_,_} -> ok end,
- Sender ! {send_please, Pid, to_dead},
- {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first_trace(),
- receive_nothing(),
+ F6 = fun (Pat) ->
+ set_trace_pattern(send, Pat, []),
+ Sender ! {send_please, Pid, to_dead},
+ {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first_trace(),
+ receive_nothing()
+ end,
+ lists:foreach(F6, [no,
+ [{[Pid,to_dead],[],[]}],
+ [{['_','_'],[],[]}],
+ true]),
%% Check that a message sent to unknown registrated process is traced.
BadargSender = fun_spawn(fun sender/0),
@@ -299,8 +380,22 @@ send_trace(Config) when is_list(Config) ->
Sender ! {send_please, self(), to_myself_again},
receive to_myself_again -> ok end,
receive_nothing(),
+
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [global])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [local])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [meta])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [{meta,self()}])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [call_count])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, true, [call_time])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, restart, [])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, pause, [])),
+
+ %% Done.
ok.
+set_trace_pattern(_, no, _) -> 0;
+set_trace_pattern(MSA, Pat, Flg) -> erlang:trace_pattern(MSA, Pat, Flg).
+
%% Test trace(Pid, How, [procs]).
procs_trace(Config) when is_list(Config) ->
Name = list_to_atom(atom_to_list(?MODULE)++"_procs_trace"),
@@ -1441,8 +1536,8 @@ receive_nothing() ->
receive
Any ->
ct:fail({unexpected_message, Any})
- after 200 ->
- ok
+ after 100 ->
+ ok
end.