aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/erlang.xml176
-rw-r--r--erts/emulator/beam/beam_bp.c11
-rw-r--r--erts/emulator/beam/beam_bp.h8
-rw-r--r--erts/emulator/beam/beam_emu.c2
-rw-r--r--erts/emulator/beam/bif.c2
-rw-r--r--erts/emulator/beam/dist.c8
-rw-r--r--erts/emulator/beam/erl_alloc.c2
-rw-r--r--erts/emulator/beam/erl_bif_ddll.c2
-rw-r--r--erts/emulator/beam/erl_bif_trace.c139
-rw-r--r--erts/emulator/beam/erl_db.c3
-rw-r--r--erts/emulator/beam/erl_db_util.c411
-rw-r--r--erts/emulator/beam/erl_db_util.h8
-rw-r--r--erts/emulator/beam/erl_gc.c2
-rw-r--r--erts/emulator/beam/erl_hl_timer.c8
-rw-r--r--erts/emulator/beam/erl_message.c103
-rw-r--r--erts/emulator/beam/erl_message.h8
-rw-r--r--erts/emulator/beam/erl_msacc.c2
-rw-r--r--erts/emulator/beam/erl_nif.c27
-rw-r--r--erts/emulator/beam/erl_process.c10
-rw-r--r--erts/emulator/beam/erl_time_sup.c2
-rw-r--r--erts/emulator/beam/erl_trace.c149
-rw-r--r--erts/emulator/beam/erl_trace.h11
-rw-r--r--erts/emulator/beam/global.h22
-rw-r--r--erts/emulator/beam/io.c74
-rw-r--r--erts/emulator/beam/sys.h3
-rw-r--r--erts/emulator/beam/utils.c2
-rw-r--r--erts/emulator/hipe/hipe_bif0.c24
-rw-r--r--erts/emulator/hipe/hipe_native_bif.c2
-rw-r--r--erts/emulator/test/trace_SUITE.erl249
-rw-r--r--erts/preloaded/src/erlang.erl18
-rw-r--r--lib/runtime_tools/src/dbg.erl29
-rw-r--r--lib/runtime_tools/test/dbg_SUITE.erl222
32 files changed, 1271 insertions, 468 deletions
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 3276bc34b0..bdaa6caa15 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -9034,16 +9034,16 @@ timestamp() ->
<type name="trace_info_flag"/>
<type name="trace_match_spec"/>
<desc>
- <p>Returns trace information about a port, process or function.</p>
- <p>To get information about a port or process,
- <c><anno>PidPortOrFunc</anno></c> is to
+ <p>Returns trace information about a port, process, function or event.</p>
+ <p><em>To get information about a port or process</em>,
+ <c><anno>PidPortFuncEvent</anno></c> is to
be a process identifier (pid), port identifier or one of
the atoms <c>new</c>, <c>new_processes</c>, <c>new_ports</c>.
The atom <c>new</c> or <c>new_processes</c> means that the default trace
state for processes to be created is returned. The atom <c>new_ports</c>
means that the default trace state for ports to be created is returned.
</p>
- <p>The following <c>Item</c>s are valid:</p>
+ <p>The following <c>Item</c>s are valid for ports and processes:</p>
<taglist>
<tag><c>flags</c></tag>
<item>
@@ -9067,12 +9067,15 @@ timestamp() ->
value is <c>[]</c>.</p>
</item>
</taglist>
- <p>To get information about a function, <c><anno>PidPortOrFunc</anno></c> is to
+ <p><em>To get information about a function</em>, <c><anno>PidPortFuncEvent</anno></c> is to
be the three-element tuple <c>{Module, Function, Arity}</c> or
the atom <c>on_load</c>. No wild cards are allowed. Returns
<c>undefined</c> if the function does not exist, or
- <c>false</c> if the function is not traced.</p>
- <p>The following <c>Item</c>s are valid::</p>
+ <c>false</c> if the function is not traced. If <c><anno>PidPortFuncEvent</anno></c>
+ is <c>on_load</c>, the information returned refers to
+ the default value for code that will be loaded.</p>
+
+ <p>The following <c>Item</c>s are valid for functions:</p>
<taglist>
<tag><c>traced</c></tag>
<item>
@@ -9131,39 +9134,177 @@ timestamp() ->
is active for this function.</p>
</item>
</taglist>
+ <p><em>To get information about an event</em>, <c><anno>PidPortFuncEvent</anno></c> is to
+ be one of the atoms <c>send</c> or <c>'receive'</c>.</p>
+ <p>The only valid <c>Item</c> for events is:</p>
+ <taglist>
+ <tag><c>match_spec</c></tag>
+ <item>
+ <p>Returns the match specification for this event, if it
+ has one, or <c>true</c> if no match specification has been
+ set.</p>
+ </item>
+ </taglist>
<p>The return value is <c>{<anno>Item</anno>, Value}</c>, where
<c>Value</c> is the requested information as described earlier.
If a pid for a dead process was given, or the name of a
non-existing function, <c>Value</c> is <c>undefined</c>.</p>
- <p>If <c><anno>PidPortOrFunc</anno></c> is <c>on_load</c>, the information
- returned refers to the default value for code that will be
- loaded.</p>
</desc>
</func>
<func>
<name name="trace_pattern" arity="2" clause_i="1"/>
- <fsummary>Sets trace patterns for global call tracing.</fsummary>
+ <fsummary>Sets trace patterns for call, send or 'receive' tracing.</fsummary>
<type name="trace_pattern_mfa"/>
<type name="trace_match_spec"/>
<desc>
<p>The same as
- <seealso marker="#trace_pattern/3">erlang:trace_pattern(MFA, MatchSpec, [])</seealso>,
+ <seealso marker="#trace_pattern/3">erlang:trace_pattern(Event, MatchSpec, [])</seealso>,
retained for backward compatibility.</p>
</desc>
</func>
<func>
- <name name="trace_pattern" arity="3"/>
+ <name name="trace_pattern" arity="3" clause_i="1"/>
+ <fsummary>Sets trace pattern for message sending.</fsummary>
+ <type name="trace_match_spec"/>
+ <desc>
+ <p>Sets trace pattern for <em>message sending</em>.
+ Must be combined with
+ <seealso marker="#trace/3">erlang:trace/3</seealso>
+ to set the <c>send</c> trace flag for one or more processes.
+ By default all messages, sent from <c>send</c> traced processes,
+ are traced. Use <c>erlang:trace_pattern/3</c> to limit
+ traced send events based on the message content, the sender
+ and/or the receiver.</p>
+ <p>Argument <c><anno>MatchSpec</anno></c> can take the
+ following forms:</p>
+ <taglist>
+ <tag><c><anno>MatchSpecList</anno></c></tag>
+ <item>
+ <p>A list of match specifications. The matching is done
+ on the list <c>[Receiver, Msg]</c>. <c>Receiver</c>
+ is the process or port identity of the receiver and
+ <c>Msg</c> is the message term. The pid of the sending
+ process can be accessed with the guard function
+ <c>self/0</c>. An empty list is the same as <c>true</c>.
+ See the users guide section
+ <seealso marker="erts:match_spec">Match Specifications in Erlang</seealso>
+ for more information.</p>
+ </item>
+ <tag><c>true</c></tag>
+ <item>
+ <p>Enables tracing for all sent messages (from <c>send</c>
+ traced processes). Any match specification is
+ removed. <em>This is the default</em>.</p>
+ </item>
+ <tag><c>false</c></tag>
+ <item>
+ <p>Disables tracing for all sent messages.
+ Any match specification is removed.</p>
+ </item>
+ </taglist>
+ <p>Argument <c><anno>FlagList</anno></c> must be <c>[]</c>
+ for send tracing.</p>
+ <p>The return value is always <c>1</c>.</p>
+ <p>Example; only trace messages to a specific process <c>Pid</c>:</p>
+ <pre>
+> <input>erlang:trace_pattern(send, [{[Pid, '_'],[],[]}], []).</input>
+1</pre>
+ <p>Only trace messages matching <c>{reply, _}</c>:</p>
+ <pre>
+> <input>erlang:trace_pattern(send, [{['_', {reply,'_'}],[],[]}], []).</input>
+1</pre>
+ <p>Only trace messages sent to the sender itself:</p>
+ <pre>
+> <input>erlang:trace_pattern(send, [{['$1', '_'],[{'=:=','$1',{self}}],[]}], []).</input>
+1</pre>
+ <p>Only trace messages sent to other nodes:</p>
+ <pre>
+> <input>erlang:trace_pattern(send, [{['$1', '_'],[{'=/=',{node,'$1'},{node}}],[]}], []).</input>
+1</pre>
+ <note><p>A match specification for <c>send</c> trace can use
+ all guard and body functions except <c>caller</c>.</p></note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="trace_pattern" arity="3" clause_i="2"/>
+ <fsummary>Sets trace pattern for tracing of message receiving.</fsummary>
+ <type name="trace_match_spec"/>
+ <desc>
+ <p></p>
+ <p>Sets trace pattern for <em>message receiving</em>.
+ Must be combined with
+ <seealso marker="#trace/3">erlang:trace/3</seealso>
+ to set the <c>'receive'</c> trace flag for one or more processes.
+ By default all messages, received by <c>'receive'</c> traced processes,
+ are traced. Use <c>erlang:trace_pattern/3</c> to limit
+ traced receive events based on the message content, the sender
+ and/or the receiver.</p>
+ <p>Argument <c><anno>MatchSpec</anno></c> can take the
+ following forms:</p>
+ <taglist>
+ <tag><c><anno>MatchSpecList</anno></c></tag>
+ <item>
+ <p>A list of match specifications. The matching is done
+ on the list <c>[Node, Sender, Msg]</c>. <c>Node</c>
+ is the node name of the sender. <c>Sender</c> is the
+ process or port identity of the sender, or the atom
+ <c>undefined</c> if the sender is not known (which may
+ be the case for remote senders). <c>Msg</c> is the
+ message term. The pid of the receiving process can be
+ accessed with the guard function <c>self/0</c>. An empty
+ list is the same as <c>true</c>. See the users guide section
+ <seealso marker="erts:match_spec">Match Specifications in Erlang</seealso>
+ for more information.</p>
+ </item>
+ <tag><c>true</c></tag>
+ <item>
+ <p>Enables tracing for all received messages (to <c>'receive'</c>
+ traced processes). Any match specification is
+ removed. <em>This is the default</em>.</p>
+ </item>
+ <tag><c>false</c></tag>
+ <item>
+ <p>Disables tracing for all received messages.
+ Any match specification is removed.</p>
+ </item>
+ </taglist>
+ <p>Argument <c><anno>FlagList</anno></c> must be <c>[]</c>
+ for receive tracing.</p>
+ <p>The return value is always <c>1</c>.</p>
+ <p>Example; only trace messages from a specific process <c>Pid</c>:</p>
+ <pre>
+> <input>erlang:trace_pattern('receive', [{['_',Pid, '_'],[],[]}], []).</input>
+1</pre>
+ <p>Only trace messages matching <c>{reply, _}</c>:</p>
+ <pre>
+> <input>erlang:trace_pattern('receive', [{['_','_', {reply,'_'}],[],[]}], []).</input>
+1</pre>
+ <p>Only trace messages from other nodes:</p>
+ <pre>
+> <input>erlang:trace_pattern('receive', [{['$1', '_', '_'],[{'=/=','$1',{node}}],[]}], []).</input>
+1</pre>
+ <note><p>A match specification for <c>'receive'</c> trace can
+ use all guard and body functions except <c>caller,
+ is_seq_trace, get_seq_token, set_seq_token, enable_trace,
+ disable_trace, trace, silent</c> and <c>process_dump</c>.</p></note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="trace_pattern" arity="3" clause_i="3"/>
<fsummary>Sets trace patterns for tracing of function calls.</fsummary>
<type name="trace_pattern_mfa"/>
<type name="trace_match_spec"/>
<type name="trace_pattern_flag"/>
<desc>
- <p>Enables or disables call tracing for
- one or more functions. Must be combined with
+ <p>Enables or disables <em>call tracing</em> for one or more functions.
+ Must be combined with
<seealso marker="#trace/3">erlang:trace/3</seealso>
- to set the <c>call</c> trace flag for one or more processes.</p>
+ to set the <c>call</c> trace flag
+ for one or more processes.</p>
<p>Conceptually, call tracing works as follows. Inside
the Erlang Virtual Machine, a set of processes and
a set of functions are to be traced. If a traced process
@@ -9218,7 +9359,8 @@ timestamp() ->
</item>
<tag><c>true</c></tag>
<item>
- <p>Enables tracing for the matching functions.</p>
+ <p>Enables tracing for the matching functions.
+ Any match specification is removed.</p>
</item>
<tag><c><anno>MatchSpecList</anno></c></tag>
<item>
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
index 2ee98ed7b5..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
@@ -1432,7 +1435,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
g = (GenericBp *) pc[-4];
if (g == 0) {
int i;
- if (count_op == erts_break_reset || count_op == erts_break_stop) {
+ if (count_op == ERTS_BREAK_RESTART || count_op == ERTS_BREAK_PAUSE) {
/* Do not insert a new breakpoint */
return;
}
@@ -1456,7 +1459,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
MatchSetUnref(bp->meta_ms);
bp_meta_unref(bp->meta_tracer);
} else if (common & ERTS_BPF_COUNT) {
- if (count_op == erts_break_stop) {
+ if (count_op == ERTS_BREAK_PAUSE) {
bp->flags &= ~ERTS_BPF_COUNT_ACTIVE;
} else {
bp->flags |= ERTS_BPF_COUNT_ACTIVE;
@@ -1468,7 +1471,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
BpDataTime* bdt = bp->time;
Uint i = 0;
- if (count_op == erts_break_stop) {
+ if (count_op == ERTS_BREAK_PAUSE) {
bp->flags &= ~ERTS_BPF_TIME_TRACE_ACTIVE;
} else {
bp->flags |= ERTS_BPF_TIME_TRACE_ACTIVE;
diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h
index 08641b86d6..9c2fc007a2 100644
--- a/erts/emulator/beam/beam_bp.h
+++ b/erts/emulator/beam/beam_bp.h
@@ -86,10 +86,10 @@ typedef struct generic_bp {
#endif
enum erts_break_op{
- erts_break_nop = 0, /* Must be false */
- erts_break_set = !0, /* Must be true */
- erts_break_reset,
- erts_break_stop
+ ERTS_BREAK_NOP = 0, /* Must be false */
+ ERTS_BREAK_SET = !0, /* Must be true */
+ ERTS_BREAK_RESTART,
+ ERTS_BREAK_PAUSE
};
typedef Uint32 ErtsBpIndex;
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 59112e1e43..99eb26bec5 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -2200,7 +2200,7 @@ void process_main(void)
PreFetch(0, next);
if (IS_TRACED_FL(c_p, F_TRACE_RECEIVE)) {
- trace_receive(c_p, am_timeout);
+ trace_receive(c_p, am_clock_service, am_timeout, NULL);
}
if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) {
save_calls(c_p, &exp_timeout);
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 9d6dfefa3c..eed0702688 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -611,7 +611,7 @@ erts_queue_monitor_message(Process *p,
ref_copy = copy_struct(ref, ref_size, &hp, ohp);
tup = TUPLE5(hp, am_DOWN, ref_copy, type, item_copy, reason_copy);
- erts_queue_message(p, p_locksp, msgp, tup);
+ erts_queue_message(p, *p_locksp, msgp, tup, am_system);
}
static BIF_RETTYPE
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 277e0668a2..09c83f1117 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -397,7 +397,7 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp)
msgp = erts_alloc_message_heap(rp, &rp_locks,
3, &hp, &ohp);
tup = TUPLE2(hp, am_nodedown, name);
- erts_queue_message(rp, &rp_locks, msgp, tup);
+ erts_queue_message(rp, rp_locks, msgp, tup, am_system);
}
erts_smp_proc_unlock(rp, rp_locks);
}
@@ -1456,7 +1456,7 @@ int erts_net_message(Port *prt,
token = copy_struct(token, token_size, &hp, ohp);
}
- erts_queue_dist_message(rp, &locks, ede_copy, token);
+ erts_queue_dist_message(rp, locks, ede_copy, token, from);
if (locks)
erts_smp_proc_unlock(rp, locks);
}
@@ -1505,7 +1505,7 @@ int erts_net_message(Port *prt,
token = copy_struct(token, token_size, &hp, ohp);
}
- erts_queue_dist_message(rp, &locks, ede_copy, token);
+ erts_queue_dist_message(rp, locks, ede_copy, token, tuple[2]);
if (locks)
erts_smp_proc_unlock(rp, locks);
}
@@ -3317,7 +3317,7 @@ send_nodes_mon_msg(Process *rp,
}
ASSERT(hend == hp);
- erts_queue_message(rp, rp_locksp, mp, msg);
+ erts_queue_message(rp, *rp_locksp, mp, msg, am_system);
}
static void
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index d04977b9ae..3dc12bac51 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -3286,7 +3286,7 @@ reply_alloc_info(void *vair)
if (hp != hp_end)
erts_shrink_message_heap(&mp, rp, hp_start, hp, hp_end, &msg, 1);
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (air->req_sched == sched_id)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c
index 1e2db38442..ef77201544 100644
--- a/erts/emulator/beam/erl_bif_ddll.c
+++ b/erts/emulator/beam/erl_bif_ddll.c
@@ -1737,7 +1737,7 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type,
hp += REF_THING_SIZE;
mess = TUPLE5(hp,type,r,am_driver,driver_name,tag);
}
- erts_queue_message(proc, &rp_locks, mp, mess);
+ erts_queue_message(proc, rp_locks, mp, mess, am_system);
erts_smp_proc_unlock(proc, rp_locks);
ERTS_SMP_CHK_NO_PROC_LOCKS;
}
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index ff2018aa27..b65c0e303f 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 int
+erts_set_tracing_event_pattern(Eterm event, Binary*, int on);
+
#ifdef ERTS_SMP
static void smp_bp_finisher(void* arg);
#endif
@@ -78,6 +81,8 @@ static void new_seq_trace_token(Process* p); /* help func for seq_trace_2*/
static Eterm trace_info_pid(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_on_load(Process* p, Eterm key);
+static Eterm trace_info_event(Process* p, Eterm event, Eterm key);
+
static void reset_bif_trace(void);
static void setup_bif_trace(void);
@@ -85,14 +90,26 @@ 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];
+ErtsTracingEvent erts_receive_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;
+ erts_receive_tracing[i].on = 1;
+ erts_receive_tracing[i].match_spec = NULL;
+ }
}
/*
@@ -137,15 +154,18 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
on = 1;
} else if (Pattern == am_restart) {
match_prog_set = NULL;
- on = erts_break_reset;
+ on = ERTS_BREAK_RESTART;
} else if (Pattern == am_pause) {
match_prog_set = NULL;
- on = erts_break_stop;
- } else if ((match_prog_set = erts_match_set_compile(p, Pattern)) != NULL) {
- MatchSetRef(match_prog_set);
- on = 1;
- } else{
- goto error;
+ on = ERTS_BREAK_PAUSE;
+ } else {
+ match_prog_set = erts_match_set_compile(p, Pattern, MFA);
+ if (match_prog_set) {
+ MatchSetRef(match_prog_set);
+ on = 1;
+ } else{
+ goto error;
+ }
}
is_global = 0;
@@ -314,6 +334,11 @@ 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 (is_atom(MFA)) {
+ if (is_global || flags.breakpoint || on > ERTS_BREAK_SET) {
+ goto error;
+ }
+ matches = erts_set_tracing_event_pattern(MFA, match_prog_set, on);
}
error:
@@ -791,6 +816,8 @@ Eterm trace_info_2(BIF_ALIST_2)
if (What == am_on_load) {
res = trace_info_on_load(p, Key);
+ } else if (What == am_send || What == am_receive) {
+ res = trace_info_event(p, What, Key);
} else if (is_atom(What) || is_pid(What) || is_port(What)) {
res = trace_info_pid(p, What, Key);
} else if (is_tuple(What)) {
@@ -1280,6 +1307,42 @@ trace_info_on_load(Process* p, Eterm key)
}
}
+static Eterm
+trace_info_event(Process* p, Eterm event, Eterm key)
+{
+ ErtsTracingEvent* te;
+ Eterm retval;
+ Eterm* hp;
+
+ switch (event) {
+ case am_send: te = erts_send_tracing; break;
+ case am_receive: te = erts_receive_tracing; break;
+ default:
+ goto error;
+ }
+
+ if (key != am_match_spec)
+ goto error;
+
+ te = &te[erts_active_bp_ix()];
+
+ if (te->on) {
+ if (!te->match_spec)
+ retval = am_true;
+ else
+ retval = copy_object(MatchSetGetSource(te->match_spec), p);
+ }
+ else
+ retval = am_false;
+
+ hp = HAlloc(p, 3);
+ return TUPLE2(hp, key, retval);
+
+ error:
+ BIF_ERROR(p, BADARG);
+}
+
+
#undef FUNC_TRACE_NOEXIST
#undef FUNC_TRACE_UNTRACED
#undef FUNC_TRACE_GLOBAL_TRACE
@@ -1307,7 +1370,7 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
for (i = 0; i < n; i++) {
BeamInstr* pc = fp[i].pc;
- Export* ep = (Export *)(((char *)(pc-3)) - offsetof(Export, code));
+ Export* ep = ErtsContainerStruct(pc, Export, code[3]);
if (on && !flags.breakpoint) {
/* Turn on global call tracing */
@@ -1468,12 +1531,57 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
}
int
+erts_set_tracing_event_pattern(Eterm event, Binary* match_spec, int on)
+{
+ ErtsBpIndex ix = erts_staging_bp_ix();
+ ErtsTracingEvent* st;
+
+ switch (event) {
+ case am_send: st = &erts_send_tracing[ix]; break;
+ case am_receive: st = &erts_receive_tracing[ix]; break;
+ default: return -1;
+ }
+
+ MatchSetUnref(st->match_spec);
+
+ st->on = on;
+ st->match_spec = match_spec;
+ MatchSetRef(match_spec);
+
+ finish_bp.current = 1; /* prepare phase not needed for event 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
+ return 1;
+}
+
+static void
+consolidate_event_tracing(ErtsTracingEvent te[])
+{
+ ErtsTracingEvent* src = &te[erts_active_bp_ix()];
+ ErtsTracingEvent* dst = &te[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)
{
ERTS_SMP_LC_ASSERT(erts_has_code_write_permission());
/*
- * Memory barriers will be issued for all processes *before*
+ * Memory barriers will be issued for all schedulers *before*
* each of the stages below. (Unless the other schedulers
* are blocked, in which case memory barriers will be issued
* when they are awaken.)
@@ -1542,6 +1650,8 @@ 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_event_tracing(erts_send_tracing);
+ consolidate_event_tracing(erts_receive_tracing);
return 0;
default:
ASSERT(0);
@@ -1556,11 +1666,10 @@ install_exp_breakpoints(BpFunctions* f)
BpFunction* fp = f->matching;
Uint ne = f->matched;
Uint i;
- Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
for (i = 0; i < ne; i++) {
BeamInstr* pc = fp[i].pc;
- Export* ep = (Export *) (((char *)pc)-offset);
+ Export* ep = ErtsContainerStruct(pc, Export, code[3]);
ep->addressv[code_ix] = pc;
}
@@ -1573,11 +1682,10 @@ uninstall_exp_breakpoints(BpFunctions* f)
BpFunction* fp = f->matching;
Uint ne = f->matched;
Uint i;
- Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
for (i = 0; i < ne; i++) {
BeamInstr* pc = fp[i].pc;
- Export* ep = (Export *) (((char *)pc)-offset);
+ Export* ep = ErtsContainerStruct(pc, Export, code[3]);
if (ep->addressv[code_ix] != pc) {
continue;
@@ -1594,11 +1702,10 @@ clean_export_entries(BpFunctions* f)
BpFunction* fp = f->matching;
Uint ne = f->matched;
Uint i;
- Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
for (i = 0; i < ne; i++) {
BeamInstr* pc = fp[i].pc;
- Export* ep = (Export *) (((char *)pc)-offset);
+ Export* ep = ErtsContainerStruct(pc, Export, code[3]);
if (ep->addressv[code_ix] == pc) {
continue;
@@ -2277,7 +2384,7 @@ reply_trace_delivered_all(void *vtdarp)
#ifdef ERTS_SMP
erts_send_sys_msg_proc(rp->common.id, rp->common.id, msg, bp);
#else
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
#endif
erts_free(ERTS_ALC_T_MISC_AUX_WORK, vtdarp);
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 615d23402b..acaca54e9a 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -2833,7 +2833,8 @@ BIF_RETTYPE ets_match_spec_run_r_3(BIF_ALIST_3)
BIF_TRAP3(bif_export[BIF_ets_match_spec_run_r_3],
BIF_P,lst,BIF_ARG_2,ret);
}
- res = db_prog_match(BIF_P, mp, CAR(list_val(lst)), NULL, 0,
+ res = db_prog_match(BIF_P, BIF_P,
+ mp, CAR(list_val(lst)), NULL, 0,
ERTS_PAM_COPY_RESULT, &dummy);
if (is_value(res)) {
hp = HAlloc(BIF_P, 2);
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 76b96637ae..95b1cd0148 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -123,6 +123,9 @@ do { \
#define TermWords(t) (((t) / (sizeof(UWord)/sizeof(Eterm))) + !!((t) % (sizeof(UWord)/sizeof(Eterm))))
+#define add_dmc_err(EINFO, STR, VAR, TERM, SEV) \
+ vadd_dmc_err(EINFO, SEV, VAR, STR, TERM)
+
static ERTS_INLINE Process *
get_proc(Process *cp, Uint32 cp_locks, Eterm id, Uint32 id_locks)
@@ -411,17 +414,27 @@ get_match_pseudo_process(Process *c_p, Uint heap_size)
{
ErtsMatchPseudoProcess *mpsp;
#ifdef ERTS_SMP
- mpsp = (ErtsMatchPseudoProcess *) c_p->scheduler_data->match_pseudo_process;
- if (mpsp)
+ ErtsSchedulerData *esdp;
+
+ esdp = c_p ? c_p->scheduler_data : erts_get_scheduler_data();
+
+ mpsp = esdp ? esdp->match_pseudo_process :
+ (ErtsMatchPseudoProcess*) erts_smp_tsd_get(match_pseudo_process_key);
+
+ if (mpsp) {
+ ASSERT(mpsp == erts_smp_tsd_get(match_pseudo_process_key));
+ ASSERT(mpsp->process.scheduler_data == esdp);
cleanup_match_pseudo_process(mpsp, 0);
+ }
else {
ASSERT(erts_smp_tsd_get(match_pseudo_process_key) == NULL);
mpsp = create_match_pseudo_process();
- c_p->scheduler_data->match_pseudo_process = (void *) mpsp;
+ if (esdp) {
+ esdp->match_pseudo_process = (void *) mpsp;
+ }
+ mpsp->process.scheduler_data = esdp;
erts_smp_tsd_set(match_pseudo_process_key, (void *) mpsp);
}
- ASSERT(mpsp == erts_smp_tsd_get(match_pseudo_process_key));
- mpsp->process.scheduler_data = c_p->scheduler_data;
#else
mpsp = match_pseudo_process;
cleanup_match_pseudo_process(mpsp, 0);
@@ -889,11 +902,7 @@ void db_match_dis(Binary *prog);
#define TRACE /* Nothing */
#define FENCE_PATTERN_SIZE 0
#endif
-static void add_dmc_err(DMCErrInfo *err_info,
- char *str,
- int variable,
- Eterm term,
- DMCErrorSeverity severity);
+static void vadd_dmc_err(DMCErrInfo*, DMCErrorSeverity, int var, const char *str, ...);
static Eterm dpm_array_to_list(Process *psp, Eterm *arr, int arity);
@@ -989,12 +998,20 @@ Eterm erts_match_set_get_source(Binary *mpsp)
}
/* This one is for the tracing */
-Binary *erts_match_set_compile(Process *p, Eterm matchexpr) {
+Binary *erts_match_set_compile(Process *p, Eterm matchexpr, Eterm MFA) {
Binary *bin;
Uint sz;
Eterm *hp;
+ Uint flags;
+
+ switch (MFA) {
+ case am_receive: flags = DCOMP_TRACE; break;
+ case am_send: flags = DCOMP_TRACE | DCOMP_ALLOW_TRACE_OPS; break;
+ default:
+ flags = DCOMP_TRACE | DCOMP_CALL_TRACE | DCOMP_ALLOW_TRACE_OPS;
+ }
- bin = db_match_set_compile(p, matchexpr, DCOMP_TRACE);
+ bin = db_match_set_compile(p, matchexpr, flags);
if (bin != NULL) {
MatchProg *prog = Binary2MatchProg(bin);
sz = size_object(matchexpr);
@@ -1124,8 +1141,8 @@ Eterm db_match_set_lint(Process *p, Eterm matchexpr, Uint flags)
int i;
if (!is_list(matchexpr)) {
- add_dmc_err(err_info, "Match programs are not in a list.",
- -1, 0UL, dmcError);
+ add_dmc_err(err_info, "Match programs are not in a list.",
+ -1, 0UL, dmcError);
goto done;
}
num_heads = 0;
@@ -1133,9 +1150,8 @@ Eterm db_match_set_lint(Process *p, Eterm matchexpr, Uint flags)
++num_heads;
if (l != NIL) { /* proper list... */
- add_dmc_err(err_info, "Match programs are not in a proper "
- "list.",
- -1, 0UL, dmcError);
+ add_dmc_err(err_info, "Match programs are not in a proper list.",
+ -1, 0UL, dmcError);
goto done;
}
@@ -1202,30 +1218,37 @@ done:
return ret;
}
-Eterm erts_match_set_run(Process *p, Binary *mpsp,
- Eterm *args, int num_args,
- enum erts_pam_run_flags in_flags,
- Uint32 *return_flags)
+/* Returns
+ * am_false if no match or
+ * if {message,false} has been called,
+ * am_true if {message,_} has NOT been called or
+ * if {message,true} has been called,
+ * Msg if {message,Msg} has been called.
+ *
+ * If return value is_not_immed
+ * then erts_match_set_release_result_trace() must be called to release it.
+ */
+Eterm erts_match_set_run_trace(Process *c_p,
+ Process *self,
+ Binary *mpsp,
+ Eterm *args, int num_args,
+ enum erts_pam_run_flags in_flags,
+ Uint32 *return_flags)
{
Eterm ret;
- ret = db_prog_match(p, mpsp, NIL, args, num_args,
+ ret = db_prog_match(c_p, self, mpsp, NIL, args, num_args,
in_flags, return_flags);
-#if defined(HARDDEBUG)
- if (is_non_value(ret)) {
- erts_fprintf(stderr, "Failed\n");
- } else {
- erts_fprintf(stderr, "Returning : %T\n", ret);
+
+ ASSERT(!(is_non_value(ret) && *return_flags));
+
+ if (is_non_value(ret) || ret == am_false) {
+ erts_match_set_release_result(c_p);
+ return am_false;
}
-#endif
+ if (is_immed(ret))
+ erts_match_set_release_result(c_p);
return ret;
- /* Returns
- * THE_NON_VALUE if no match
- * am_false if {message,false} has been called,
- * am_true if {message,_} has not been called or
- * if {message,true} has been called,
- * Msg if {message,Msg} has been called.
- */
}
static Eterm erts_match_set_run_ets(Process *p, Binary *mpsp,
@@ -1234,7 +1257,8 @@ static Eterm erts_match_set_run_ets(Process *p, Binary *mpsp,
{
Eterm ret;
- ret = db_prog_match(p, mpsp, args, NULL, num_args,
+ ret = db_prog_match(p, p,
+ mpsp, args, NULL, num_args,
ERTS_PAM_COPY_RESULT,
return_flags);
#if defined(HARDDEBUG)
@@ -1730,7 +1754,9 @@ static Eterm dpm_array_to_list(Process *psp, Eterm *arr, int arity)
** the parameter 'arity' is only used if 'term' is actually an array,
** i.e. 'DCOMP_TRACE' was specified
*/
-Eterm db_prog_match(Process *c_p, Binary *bprog,
+Eterm db_prog_match(Process *c_p,
+ Process *self,
+ Binary *bprog,
Eterm term,
Eterm *termp,
int arity,
@@ -1743,10 +1769,10 @@ Eterm db_prog_match(Process *c_p, Binary *bprog,
Eterm *esp;
MatchVariable* variables;
BeamInstr *cp;
- UWord *pc = prog->text;
+ const UWord *pc = prog->text;
Eterm *ehp;
Eterm ret;
- Uint n = 0; /* To avoid warning. */
+ Uint n;
int i;
unsigned do_catch;
ErtsMatchPseudoProcess *mpsp;
@@ -1758,46 +1784,28 @@ Eterm db_prog_match(Process *c_p, Binary *bprog,
Eterm (*bif)(Process*, ...);
Eterm bif_args[3];
int fail_label;
- int atomic_trace;
#ifdef DMC_DEBUG
Uint *heap_fence;
Uint *stack_fence;
Uint save_op;
#endif /* DMC_DEBUG */
+ ERTS_UNDEF(n,0);
+ ERTS_UNDEF(current_scheduled,NULL);
+
+ ASSERT(c_p || !(in_flags & ERTS_PAM_COPY_RESULT));
+
mpsp = get_match_pseudo_process(c_p, prog->heap_size);
psp = &mpsp->process;
/* We need to lure the scheduler into believing in the pseudo process,
because of floating point exceptions. Do *after* mpsp is set!!! */
- esdp = ERTS_GET_SCHEDULER_DATA_FROM_PROC(c_p);
- ASSERT(esdp != NULL);
- current_scheduled = esdp->current_process;
+ esdp = ERTS_GET_SCHEDULER_DATA_FROM_PROC(psp);
+ if (esdp)
+ current_scheduled = esdp->current_process;
/* SMP: psp->scheduler_data is set by get_match_pseudo_process */
- atomic_trace = 0;
-#define BEGIN_ATOMIC_TRACE(p) \
- do { \
- if (! atomic_trace) { \
- erts_refc_inc(&bprog->refc, 2); \
- erts_smp_proc_unlock((p), ERTS_PROC_LOCK_MAIN); \
- erts_smp_thr_progress_block(); \
- atomic_trace = !0; \
- } \
- } while (0)
-#define END_ATOMIC_TRACE(p) \
- do { \
- if (atomic_trace) { \
- erts_smp_thr_progress_unblock(); \
- erts_smp_proc_lock((p), ERTS_PROC_LOCK_MAIN); \
- if (erts_refc_dectest(&bprog->refc, 0) == 0) {\
- erts_bin_free(bprog); \
- } \
- atomic_trace = 0; \
- } \
- } while (0)
-
#ifdef DMC_DEBUG
save_op = 0;
heap_fence = (Eterm*)((char*) mpsp->u.heap + prog->stack_offset) - 1;
@@ -2256,7 +2264,7 @@ restart:
pc += n;
break;
case matchSelf:
- *esp++ = c_p->common.id;
+ *esp++ = self->common.id;
break;
case matchWaste:
--esp;
@@ -2266,6 +2274,7 @@ restart:
break;
case matchProcessDump: {
erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0);
+ ASSERT(c_p == self);
print_process_info(ERTS_PRINT_DSBUF, (void *) dsbufp, c_p);
*esp++ = new_binary(build_proc, (byte *)dsbufp->str,
dsbufp->str_len);
@@ -2284,14 +2293,16 @@ restart:
*return_flags |= MATCH_SET_EXCEPTION_TRACE;
*esp++ = am_true;
break;
- case matchIsSeqTrace:
+ case matchIsSeqTrace:
+ ASSERT(c_p == self);
if (have_seqtrace(SEQ_TRACE_TOKEN(c_p)))
*esp++ = am_true;
else
*esp++ = am_false;
break;
case matchSetSeqToken:
- t = erts_seq_trace(c_p, esp[-1], esp[-2], 0);
+ ASSERT(c_p == self);
+ t = erts_seq_trace(c_p, esp[-1], esp[-2], 0);
if (is_non_value(t)) {
esp[-2] = FAIL_TERM;
} else {
@@ -2299,7 +2310,8 @@ restart:
}
--esp;
break;
- case matchSetSeqTokenFake:
+ case matchSetSeqTokenFake:
+ ASSERT(c_p == self);
t = seq_trace_fake(c_p, esp[-1]);
if (is_non_value(t)) {
esp[-2] = FAIL_TERM;
@@ -2308,7 +2320,8 @@ restart:
}
--esp;
break;
- case matchGetSeqToken:
+ case matchGetSeqToken:
+ ASSERT(c_p == self);
if (have_no_seqtrace(SEQ_TRACE_TOKEN(c_p)))
*esp++ = NIL;
else {
@@ -2332,49 +2345,62 @@ restart:
ASSERT(is_immed(ehp[5]));
}
break;
- case matchEnableTrace:
+ case matchEnableTrace:
+ ASSERT(c_p == self);
if ( (n = erts_trace_flag2bit(esp[-1]))) {
- BEGIN_ATOMIC_TRACE(c_p);
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
set_tracee_flags(c_p, ERTS_TRACER(c_p), 0, n);
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
esp[-1] = am_true;
} else {
esp[-1] = FAIL_TERM;
}
break;
- case matchEnableTrace2:
+ case matchEnableTrace2:
+ ASSERT(c_p == self);
n = erts_trace_flag2bit((--esp)[-1]);
esp[-1] = FAIL_TERM;
if (n) {
- BEGIN_ATOMIC_TRACE(c_p);
- if ( (tmpp = get_proc(c_p, 0, esp[0], 0))) {
+ if ( (tmpp = get_proc(c_p, ERTS_PROC_LOCK_MAIN, esp[0], ERTS_PROC_LOCKS_ALL))) {
/* Always take over the tracer of the current process */
set_tracee_flags(tmpp, ERTS_TRACER(c_p), 0, n);
- esp[-1] = am_true;
+ if (tmpp == c_p)
+ erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL_MINOR);
+ else
+ erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL);
+ esp[-1] = am_true;
}
}
break;
- case matchDisableTrace:
+ case matchDisableTrace:
+ ASSERT(c_p == self);
if ( (n = erts_trace_flag2bit(esp[-1]))) {
- BEGIN_ATOMIC_TRACE(c_p);
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
set_tracee_flags(c_p, ERTS_TRACER(c_p), n, 0);
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
esp[-1] = am_true;
} else {
esp[-1] = FAIL_TERM;
}
break;
- case matchDisableTrace2:
+ case matchDisableTrace2:
+ ASSERT(c_p == self);
n = erts_trace_flag2bit((--esp)[-1]);
esp[-1] = FAIL_TERM;
if (n) {
- BEGIN_ATOMIC_TRACE(c_p);
- if ( (tmpp = get_proc(c_p, 0, esp[0], 0))) {
+ if ( (tmpp = get_proc(c_p, ERTS_PROC_LOCK_MAIN, esp[0], ERTS_PROC_LOCKS_ALL))) {
/* Always take over the tracer of the current process */
set_tracee_flags(tmpp, ERTS_TRACER(c_p), n, 0);
- esp[-1] = am_true;
+ if (tmpp == c_p)
+ erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL_MINOR);
+ else
+ erts_smp_proc_unlock(tmpp, ERTS_PROC_LOCKS_ALL);
+ esp[-1] = am_true;
}
}
break;
- case matchCaller:
+ case matchCaller:
+ ASSERT(c_p == self);
if (!(c_p->cp) || !(cp = find_function_from_pc(c_p->cp))) {
*esp++ = am_undefined;
} else {
@@ -2386,7 +2412,8 @@ restart:
ehp[3] = make_small((Uint) cp[2]);
}
break;
- case matchSilent:
+ case matchSilent:
+ ASSERT(c_p == self);
--esp;
if (in_flags & ERTS_PAM_IGNORE_TRACE_SILENT)
break;
@@ -2401,7 +2428,8 @@ restart:
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
}
break;
- case matchTrace2:
+ case matchTrace2:
+ ASSERT(c_p == self);
{
/* disable enable */
Uint d_flags = 0, e_flags = 0; /* process trace flags */
@@ -2433,7 +2461,8 @@ restart:
ERTS_TRACER_CLEAR(&tracer);
}
break;
- case matchTrace3:
+ case matchTrace3:
+ ASSERT(c_p == self);
{
/* disable enable */
Uint d_flags = 0, e_flags = 0; /* process trace flags */
@@ -2506,15 +2535,12 @@ success:
}
#endif
- esdp->current_process = current_scheduled;
-
- END_ATOMIC_TRACE(c_p);
+ if (esdp)
+ esdp->current_process = current_scheduled;
return ret;
#undef FAIL
#undef FAIL_TERM
-#undef BEGIN_ATOMIC_TRACE
-#undef END_ATOMIC_TRACE
}
@@ -3259,20 +3285,20 @@ int erts_db_is_compiled_ms(Eterm term)
** Utility to add an error
*/
-static void add_dmc_err(DMCErrInfo *err_info,
- char *str,
- int variable,
- Eterm term,
- DMCErrorSeverity severity)
+static void vadd_dmc_err(DMCErrInfo *err_info,
+ DMCErrorSeverity severity,
+ int variable,
+ const char *str,
+ ...)
{
+ DMCError *e;
+ va_list args;
+ va_start(args, str);
+
+
/* Linked in in reverse order, to ease the formatting */
- DMCError *e = erts_alloc(ERTS_ALC_T_DB_DMC_ERROR, sizeof(DMCError));
- if (term != 0UL) {
- erts_snprintf(e->error_string, DMC_ERR_STR_LEN, str, term);
- } else {
- strncpy(e->error_string, str, DMC_ERR_STR_LEN);
- e->error_string[DMC_ERR_STR_LEN] ='\0';
- }
+ e = erts_alloc(ERTS_ALC_T_DB_DMC_ERROR, sizeof(DMCError));
+ erts_vsnprintf(e->error_string, DMC_ERR_STR_LEN, str, args);
e->variable = variable;
e->severity = severity;
e->next = err_info->first;
@@ -3282,8 +3308,11 @@ static void add_dmc_err(DMCErrInfo *err_info,
err_info->first = e;
if (severity >= dmcError)
err_info->error_added = 1;
+
+ va_end(args);
}
+
/*
** Handle one term in the match expression (not the guard)
*/
@@ -3482,24 +3511,21 @@ static void do_emit_constant(DMCContext *context, DMC_STACK_TYPE(UWord) *text,
context->stack_need = context->stack_used;
}
-#define RETURN_ERROR_X(String, X, Y, ContextP, ConstantF) \
-do { \
-if ((ContextP)->err_info != NULL) { \
- (ConstantF) = 0; \
- add_dmc_err((ContextP)->err_info, String, X, Y, dmcError); \
- return retOk; \
-} else \
- return retFail; \
-} while(0)
+#define RETURN_ERROR_X(VAR, ContextP, ConstantF, String, ARG) \
+ (((ContextP)->err_info != NULL) \
+ ? ((ConstantF) = 0, \
+ vadd_dmc_err((ContextP)->err_info, dmcError, VAR, String, ARG), \
+ retOk) \
+ : retFail)
#define RETURN_ERROR(String, ContextP, ConstantF) \
- RETURN_ERROR_X(String, -1, 0UL, ContextP, ConstantF)
+ return RETURN_ERROR_X(-1, ContextP, ConstantF, String, 0)
#define RETURN_VAR_ERROR(String, N, ContextP, ConstantF) \
- RETURN_ERROR_X(String, N, 0UL, ContextP, ConstantF)
+ return RETURN_ERROR_X(N, ContextP, ConstantF, String, 0)
#define RETURN_TERM_ERROR(String, T, ContextP, ConstantF) \
- RETURN_ERROR_X(String, -1, T, ContextP, ConstantF)
+ return RETURN_ERROR_X(-1, ContextP, ConstantF, String, T)
#define WARNING(String, ContextP) \
add_dmc_err((ContextP)->err_info, String, -1, 0UL, dmcWarning)
@@ -3765,7 +3791,7 @@ static DMCRet dmc_variable(DMCContext *context,
Uint n = db_is_variable(t);
if (n >= heap->vars_used || !heap->vars[n].is_bound) {
- RETURN_VAR_ERROR("Variable $%d is unbound.", n, context, *constant);
+ RETURN_VAR_ERROR("Variable $%%d is unbound.", n, context, *constant);
}
dmc_add_pushv_variant(context, heap, text, n);
@@ -4097,7 +4123,30 @@ static DMCRet dmc_exception_trace(DMCContext *context,
return retOk;
}
-
+static int check_trace(const char* op,
+ DMCContext *context,
+ int *constant,
+ int need_cflags,
+ int allow_in_guard,
+ DMCRet* retp)
+{
+ if (!(context->cflags & DCOMP_TRACE)) {
+ *retp = RETURN_ERROR_X(-1, context, *constant, "Special form '%s' "
+ "used in wrong dialect.", op);
+ return 0;
+ }
+ if ((context->cflags & need_cflags) != need_cflags) {
+ *retp = RETURN_ERROR_X(-1, context, *constant, "Special form '%s' "
+ "not allow for this trace event.", op);
+ return 0;
+ }
+ if (context->is_guard && !allow_in_guard) {
+ *retp = RETURN_ERROR_X(-1, context, *constant, "Special form '%s' "
+ "called in guard context.", op);
+ return 0;
+ }
+ return 1;
+}
static DMCRet dmc_is_seq_trace(DMCContext *context,
DMCHeap *heap,
@@ -4107,12 +4156,11 @@ static DMCRet dmc_is_seq_trace(DMCContext *context,
{
Eterm *p = tuple_val(t);
Uint a = arityval(*p);
+ DMCRet ret;
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'is_seq_trace' used in wrong dialect.",
- context,
- *constant);
- }
+ if (!check_trace("is_seq_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 1, &ret))
+ return ret;
+
if (a != 1) {
RETURN_TERM_ERROR("Special form 'is_seq_trace' called with "
"arguments in %T.", t, context, *constant);
@@ -4136,16 +4184,8 @@ static DMCRet dmc_set_seq_token(DMCContext *context,
DMCRet ret;
int c;
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'set_seq_token' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'set_seq_token' called in "
- "guard context.", context, *constant);
- }
+ if (!check_trace("set_seq_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
if (a != 3) {
RETURN_TERM_ERROR("Special form 'set_seq_token' called with wrong "
@@ -4182,16 +4222,11 @@ static DMCRet dmc_get_seq_token(DMCContext *context,
{
Eterm *p = tuple_val(t);
Uint a = arityval(*p);
+ DMCRet ret;
+
+ if (!check_trace("get_seq_token", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'get_seq_token' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'get_seq_token' called in "
- "guard context.", context, *constant);
- }
if (a != 1) {
RETURN_TERM_ERROR("Special form 'get_seq_token' called with "
"arguments in %T.", t, context,
@@ -4255,16 +4290,10 @@ static DMCRet dmc_process_dump(DMCContext *context,
{
Eterm *p = tuple_val(t);
Uint a = arityval(*p);
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'process_dump' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'process_dump' called in "
- "guard context.", context, *constant);
- }
+ DMCRet ret;
+
+ if (!check_trace("process_dump", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
if (a != 1) {
RETURN_TERM_ERROR("Special form 'process_dump' called with "
@@ -4288,17 +4317,8 @@ static DMCRet dmc_enable_trace(DMCContext *context,
DMCRet ret;
int c;
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'enable_trace' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'enable_trace' called in guard context.",
- context,
- *constant);
- }
+ if (!check_trace("enable_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
switch (a) {
case 2:
@@ -4347,18 +4367,9 @@ static DMCRet dmc_disable_trace(DMCContext *context,
Uint a = arityval(*p);
DMCRet ret;
int c;
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'disable_trace' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'disable_trace' called in guard context.",
- context,
- *constant);
- }
+ if (!check_trace("disable_trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
switch (a) {
case 2:
@@ -4408,17 +4419,8 @@ static DMCRet dmc_trace(DMCContext *context,
DMCRet ret;
int c;
-
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'trace' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'trace' called in guard context.",
- context,
- *constant);
- }
+ if (!check_trace("trace", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
switch (a) {
case 3:
@@ -4479,16 +4481,11 @@ static DMCRet dmc_caller(DMCContext *context,
{
Eterm *p = tuple_val(t);
Uint a = arityval(*p);
+ DMCRet ret;
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'caller' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'caller' called in "
- "guard context.", context, *constant);
- }
+ if (!check_trace("caller", context, constant,
+ (DCOMP_CALL_TRACE|DCOMP_ALLOW_TRACE_OPS), 0, &ret))
+ return ret;
if (a != 1) {
RETURN_TERM_ERROR("Special form 'caller' called with "
@@ -4514,15 +4511,8 @@ static DMCRet dmc_silent(DMCContext *context,
DMCRet ret;
int c;
- if (!(context->cflags & DCOMP_TRACE)) {
- RETURN_ERROR("Special form 'silent' used in wrong dialect.",
- context,
- *constant);
- }
- if (context->is_guard) {
- RETURN_ERROR("Special form 'silent' called in "
- "guard context.", context, *constant);
- }
+ if (!check_trace("silent", context, constant, DCOMP_ALLOW_TRACE_OPS, 0, &ret))
+ return ret;
if (a != 2) {
RETURN_TERM_ERROR("Special form 'silent' called with wrong "
@@ -5062,11 +5052,14 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
return THE_NON_VALUE;
}
if (trace) {
- lint_res = db_match_set_lint(p, spec, DCOMP_TRACE | DCOMP_FAKE_DESTRUCTIVE);
- mps = db_match_set_compile(p, spec, DCOMP_TRACE | DCOMP_FAKE_DESTRUCTIVE);
+ const Uint cflags = (DCOMP_TRACE | DCOMP_FAKE_DESTRUCTIVE |
+ DCOMP_CALL_TRACE | DCOMP_ALLOW_TRACE_OPS);
+ lint_res = db_match_set_lint(p, spec, cflags);
+ mps = db_match_set_compile(p, spec, cflags);
} else {
- lint_res = db_match_set_lint(p, spec, DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE);
- mps = db_match_set_compile(p, spec, DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE);
+ const Uint cflags = (DCOMP_TABLE | DCOMP_FAKE_DESTRUCTIVE);
+ lint_res = db_match_set_lint(p, spec, cflags);
+ mps = db_match_set_compile(p, spec, cflags);
}
if (mps == NULL) {
@@ -5099,7 +5092,8 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
}
save_cp = p->cp;
p->cp = NULL;
- res = erts_match_set_run(p, mps, arr, n,
+ res = erts_match_set_run_trace(p, p,
+ mps, arr, n,
ERTS_PAM_COPY_RESULT|ERTS_PAM_IGNORE_TRACE_SILENT,
&ret_flags);
p->cp = save_cp;
@@ -5182,7 +5176,8 @@ Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog,
obj = db_alloc_tmp_uncompressed(tb, obj);
}
- res = db_prog_match(c_p, bprog, make_tuple(obj->tpl), NULL, 0,
+ res = db_prog_match(c_p, c_p,
+ bprog, make_tuple(obj->tpl), NULL, 0,
ERTS_PAM_COPY_RESULT|ERTS_PAM_CONTIGUOUS_TUPLE, &dummy);
if (is_value(res) && hpp!=NULL) {
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 5d7946a525..60f7067d70 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -425,6 +425,11 @@ typedef struct dmc_err_info {
#define DCOMP_FAKE_DESTRUCTIVE ((Uint) 8) /* When this is active, no setting of
trace control words or seq_trace tokens will be done. */
+/* Allow lock seizing operations on the tracee and 3rd party processes */
+#define DCOMP_ALLOW_TRACE_OPS ((Uint) 0x10)
+
+/* This is call trace */
+#define DCOMP_CALL_TRACE ((Uint) 0x20)
Binary *db_match_compile(Eterm *matchexpr, Eterm *guards,
Eterm *body, int num_matches,
@@ -435,7 +440,8 @@ Binary *db_match_compile(Eterm *matchexpr, Eterm *guards,
Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog,
int all, DbTerm* obj, Eterm** hpp, Uint extra);
-Eterm db_prog_match(Process *p, Binary *prog, Eterm term,
+Eterm db_prog_match(Process *p, Process *self,
+ Binary *prog, Eterm term,
Eterm *termp, int arity,
enum erts_pam_run_flags in_flags,
Uint32 *return_flags /* Zeroed on enter */);
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index e21fba6bf1..bed7e668d7 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -2938,7 +2938,7 @@ reply_gc_info(void *vgcirp)
hpp = &hp;
}
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (gcirp->req_sched == esdp->no)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c
index 8e201d5711..c418762578 100644
--- a/erts/emulator/beam/erl_hl_timer.c
+++ b/erts/emulator/beam/erl_hl_timer.c
@@ -1247,8 +1247,8 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs)
if (!ERTS_PROC_IS_EXITING(proc)) {
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = tmr->btm.bp;
- erts_queue_message(proc, &proc_locks, mp,
- tmr->btm.message);
+ erts_queue_message(proc, proc_locks, mp,
+ tmr->btm.message, am_clock_service);
erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_SEND);
queued_message = 1;
proc_locks &= ~ERTS_PROC_LOCKS_MSG_SEND;
@@ -1980,7 +1980,7 @@ access_sched_local_btm(Process *c_p, Eterm pid,
ERTS_HLT_ASSERT(hp + (async ? 4 : 3) == hp_end);
- erts_queue_message(proc, &proc_locks, mp, msg);
+ erts_queue_message(proc, proc_locks, mp, msg, am_clock_service);
if (c_p)
proc_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -2111,7 +2111,7 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp,
msg = TUPLE3(hp, tag, tref, res);
- erts_queue_message(c_p, &proc_locks, mp, msg);
+ erts_queue_message(c_p, proc_locks, mp, msg, am_clock_service);
proc_locks &= ~ERTS_PROC_LOCK_MAIN;
if (proc_locks)
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 9beff52835..9bb6e40a11 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -32,6 +32,7 @@
#include "erl_process.h"
#include "erl_binary.h"
#include "dtrace-wrapper.h"
+#include "beam_bp.h"
ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message_ref,
ErtsMessageRef,
@@ -251,9 +252,10 @@ erts_realloc_shrink_message(ErtsMessage *mp, Uint sz, Eterm *brefs, Uint brefs_s
void
erts_queue_dist_message(Process *rcvr,
- ErtsProcLocks *rcvr_locks,
+ ErtsProcLocks rcvr_locks,
ErtsDistExternal *dist_ext,
- Eterm token)
+ Eterm token,
+ Eterm from)
{
ErtsMessage* mp;
#ifdef USE_VM_PROBES
@@ -265,7 +267,7 @@ erts_queue_dist_message(Process *rcvr,
erts_aint_t state;
#endif
- ERTS_SMP_LC_ASSERT(*rcvr_locks == erts_proc_lc_my_proc_locks(rcvr));
+ ERTS_SMP_LC_ASSERT(rcvr_locks == erts_proc_lc_my_proc_locks(rcvr));
mp = erts_alloc_message(0, NULL);
mp->data.dist_ext = dist_ext;
@@ -280,10 +282,10 @@ erts_queue_dist_message(Process *rcvr,
ERL_MESSAGE_TOKEN(mp) = token;
#ifdef ERTS_SMP
- if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) {
+ if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ)) {
if (erts_smp_proc_trylock(rcvr, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
ErtsProcLocks need_locks = ERTS_PROC_LOCK_MSGQ;
- if (*rcvr_locks & ERTS_PROC_LOCK_STATUS) {
+ if (rcvr_locks & ERTS_PROC_LOCK_STATUS) {
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_STATUS);
need_locks |= ERTS_PROC_LOCK_STATUS;
}
@@ -293,7 +295,7 @@ erts_queue_dist_message(Process *rcvr,
state = erts_smp_atomic32_read_acqb(&rcvr->state);
if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) {
- if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
/* Drop message if receiver is exiting or has a pending exit ... */
erts_cleanup_messages(mp);
@@ -301,10 +303,13 @@ erts_queue_dist_message(Process *rcvr,
else
#endif
if (IS_TRACED_FL(rcvr, F_TRACE_RECEIVE)) {
+ if (from == am_Empty)
+ from = dist_ext->dep->sysname;
+
/* Ahh... need to decode it in order to trace it... */
- if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
- if (!erts_decode_dist_message(rcvr, *rcvr_locks, mp, 0))
+ if (!erts_decode_dist_message(rcvr, rcvr_locks, mp, 0))
erts_free_message(mp);
else {
Eterm msg = ERL_MESSAGE_TERM(mp);
@@ -324,7 +329,7 @@ erts_queue_dist_message(Process *rcvr,
tok_label, tok_lastcnt, tok_serial);
}
#endif
- erts_queue_message(rcvr, rcvr_locks, mp, msg);
+ erts_queue_message(rcvr, rcvr_locks, mp, msg, from);
}
}
else {
@@ -351,12 +356,12 @@ erts_queue_dist_message(Process *rcvr,
LINK_MESSAGE(rcvr, mp, &mp->next, 1);
- if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ))
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
erts_proc_notify_new_message(rcvr,
#ifdef ERTS_SMP
- *rcvr_locks
+ rcvr_locks
#else
0
#endif
@@ -366,14 +371,15 @@ erts_queue_dist_message(Process *rcvr,
/* Add messages last in message queue */
static Sint
-queue_messages(Process *c_p,
- Process* receiver,
+queue_messages(Process* receiver,
erts_aint32_t *receiver_state,
- ErtsProcLocks *receiver_locks,
+ ErtsProcLocks receiver_locks,
ErtsMessage* first,
ErtsMessage** last,
- Uint len)
+ Uint len,
+ Eterm from)
{
+ ErtsTracingEvent* te;
Sint res;
int locked_msgq = 0;
erts_aint32_t state;
@@ -386,12 +392,12 @@ queue_messages(Process *c_p,
#ifdef ERTS_SMP
#ifdef ERTS_ENABLE_LOCK_CHECK
ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(receiver) < ERTS_PROC_LOCK_MSGQ ||
- *receiver_locks == erts_proc_lc_my_proc_locks(receiver));
+ receiver_locks == erts_proc_lc_my_proc_locks(receiver));
#endif
- if (!(*receiver_locks & ERTS_PROC_LOCK_MSGQ)) {
+ if (!(receiver_locks & ERTS_PROC_LOCK_MSGQ)) {
if (erts_smp_proc_trylock(receiver, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
- ErtsProcLocks need_locks = ERTS_PROC_LOCK_MSGQ;
+ ErtsProcLocks need_locks;
if (receiver_state)
state = *receiver_state;
@@ -400,10 +406,11 @@ queue_messages(Process *c_p,
if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))
goto exiting;
- if (*receiver_locks & ERTS_PROC_LOCK_STATUS) {
- erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_STATUS);
- need_locks |= ERTS_PROC_LOCK_STATUS;
+ need_locks = receiver_locks & (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ if (need_locks) {
+ erts_smp_proc_unlock(receiver, need_locks);
}
+ need_locks |= ERTS_PROC_LOCK_MSGQ;
erts_smp_proc_lock(receiver, need_locks);
}
locked_msgq = 1;
@@ -426,7 +433,7 @@ queue_messages(Process *c_p,
res = receiver->msg.len;
#ifdef ERTS_SMP
- if (*receiver_locks & ERTS_PROC_LOCK_MAIN) {
+ if (receiver_locks & ERTS_PROC_LOCK_MAIN) {
/*
* We move 'in queue' to 'private queue' and place
* message at the end of 'private queue' in order
@@ -445,7 +452,10 @@ queue_messages(Process *c_p,
LINK_MESSAGE(receiver, first, last, len);
}
- if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) {
+ if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)
+ && (te = &erts_receive_tracing[erts_active_bp_ix()],
+ te->on)) {
+
ErtsMessage *msg = first;
#ifdef USE_VM_PROBES
@@ -468,19 +478,18 @@ queue_messages(Process *c_p,
tok_label, tok_lastcnt, tok_serial);
}
#endif
-
while (msg) {
- trace_receive(receiver, ERL_MESSAGE_TERM(msg));
+ trace_receive(receiver, from, ERL_MESSAGE_TERM(msg), te);
msg = msg->next;
}
}
-
- if (locked_msgq)
+ if (locked_msgq) {
erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ);
+ }
#ifdef ERTS_SMP
- erts_proc_notify_new_message(receiver, *receiver_locks);
+ erts_proc_notify_new_message(receiver, receiver_locks);
#else
erts_proc_notify_new_message(receiver, 0);
ERTS_HOLE_CHECK(receiver);
@@ -489,31 +498,31 @@ queue_messages(Process *c_p,
}
static Sint
-queue_message(Process *c_p,
- Process* receiver,
+queue_message(Process* receiver,
erts_aint32_t *receiver_state,
- ErtsProcLocks *receiver_locks,
- ErtsMessage* mp, Eterm msg)
+ ErtsProcLocks receiver_locks,
+ ErtsMessage* mp, Eterm msg, Eterm from)
{
ERL_MESSAGE_TERM(mp) = msg;
- return queue_messages(c_p, receiver, receiver_state, receiver_locks,
- mp, &mp->next, 1 );
+ return queue_messages(receiver, receiver_state, receiver_locks,
+ mp, &mp->next, 1, from);
}
Sint
-erts_queue_message(Process* receiver, ErtsProcLocks *receiver_locks,
- ErtsMessage* mp, Eterm msg)
+erts_queue_message(Process* receiver, ErtsProcLocks receiver_locks,
+ ErtsMessage* mp, Eterm msg, Eterm from)
{
- return queue_message(NULL, receiver, NULL, receiver_locks, mp, msg);
+ return queue_message(receiver, NULL, receiver_locks, mp, msg, from);
}
Sint
-erts_queue_messages(Process* receiver, ErtsProcLocks *receiver_locks,
- ErtsMessage* first, ErtsMessage** last, Uint len)
+erts_queue_messages(Process* receiver, ErtsProcLocks receiver_locks,
+ ErtsMessage* first, ErtsMessage** last, Uint len,
+ Eterm from)
{
- return queue_messages(NULL, receiver, NULL, receiver_locks,
- first, last, len);
+ return queue_messages(receiver, NULL, receiver_locks,
+ first, last, len, from);
}
void
@@ -830,11 +839,11 @@ erts_send_message(Process* sender,
#ifdef USE_VM_PROBES
ERL_MESSAGE_DT_UTAG(mp) = utag;
#endif
- res = queue_message(sender,
- receiver,
+ res = queue_message(receiver,
&receiver_state,
- receiver_locks,
- mp, message);
+ *receiver_locks,
+ mp, message,
+ sender->common.id);
BM_SWAP_TIMER(send,system);
@@ -891,7 +900,7 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp,
seq_trace_output(token, save, SEQ_TRACE_SEND, to->common.id, NULL);
temptoken = copy_struct(token, sz_token, &hp, ohp);
ERL_MESSAGE_TOKEN(mp) = temptoken;
- erts_queue_message(to, to_locksp, mp, save);
+ erts_queue_message(to, *to_locksp, mp, save, am_system);
} else {
sz_from = IS_CONST(from) ? 0 : size_object(from);
#ifdef SHCOPY_SEND
@@ -913,7 +922,7 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp,
? from
: copy_struct(from, sz_from, &hp, ohp));
save = TUPLE3(hp, am_EXIT, from_copy, mess);
- erts_queue_message(to, to_locksp, mp, save);
+ erts_queue_message(to, *to_locksp, mp, save, am_system);
}
}
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 608cf552a2..851ac37fda 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -295,10 +295,10 @@ ErlHeapFragment* new_message_buffer(Uint);
ErlHeapFragment* erts_resize_message_buffer(ErlHeapFragment *, Uint,
Eterm *, Uint);
void free_message_buffer(ErlHeapFragment *);
-void erts_queue_dist_message(Process*, ErtsProcLocks*, ErtsDistExternal *, Eterm);
-Sint erts_queue_message(Process*, ErtsProcLocks*,ErtsMessage*, Eterm);
-Sint erts_queue_messages(Process*, ErtsProcLocks*,
- ErtsMessage*, ErtsMessage**, Uint);
+void erts_queue_dist_message(Process*, ErtsProcLocks, ErtsDistExternal *, Eterm, Eterm);
+Sint erts_queue_message(Process*, ErtsProcLocks,ErtsMessage*, Eterm, Eterm);
+Sint erts_queue_messages(Process*, ErtsProcLocks,
+ ErtsMessage*, ErtsMessage**, Uint, Eterm);
void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm);
Sint erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned);
void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp);
diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c
index d0f305900a..0e625f213b 100644
--- a/erts/emulator/beam/erl_msacc.c
+++ b/erts/emulator/beam/erl_msacc.c
@@ -257,7 +257,7 @@ static void send_reply(ErtsMsAcc *msacc, ErtsMSAccReq *msaccrp) {
if (msacc->unmanaged) erts_mtx_unlock(&msacc->mtx);
- erts_queue_message(rp, &rp_locks, msgp, msg);
+ erts_queue_message(rp, rp_locks, msgp, msg, am_system);
if (esdp && msaccrp->req_sched == esdp->no)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index dc83a780a2..a695a028ba 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -366,7 +366,7 @@ int erts_flush_trace_messages(Process *c_p, ErtsProcLocks c_p_locks)
rp_locks = 0;
if (rp->common.id == c_p->common.id)
rp_locks = c_p_locks;
- erts_queue_messages(rp, &rp_locks, first, last, len);
+ erts_queue_messages(rp, rp_locks, first, last, len, c_p->common.id);
if (rp->common.id == c_p->common.id)
rp_locks &= ~c_p_locks;
if (rp_locks)
@@ -405,7 +405,10 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
ErlNifEnv* msg_env, ERL_NIF_TERM msg)
{
struct enif_msg_environment_t* menv = (struct enif_msg_environment_t*)msg_env;
- ErtsProcLocks rp_locks = 0, lc_locks = 0, c_p_locks = ERTS_PROC_LOCK_MAIN;
+ ErtsProcLocks rp_locks = 0;
+#ifdef ERTS_SMP
+ ErtsProcLocks lc_locks = 0;
+#endif
Process* rp;
Process* c_p;
ErtsMessage *mp;
@@ -417,7 +420,7 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
if (env != NULL) {
c_p = env->proc;
if (receiver == c_p->common.id) {
- rp_locks = c_p_locks;
+ rp_locks = ERTS_PROC_LOCK_MAIN;
flush_me = 1;
}
}
@@ -470,14 +473,8 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND))
trace_send(c_p, receiver, msg);
-
-#ifndef ERTS_SMP
}
-#endif
-
- erts_queue_message(rp, &rp_locks, mp, msg);
#ifdef ERTS_SMP
- }
else {
/* This clause is taken when the nif is called in the context
of a traced process. We do not know which locks we have
@@ -502,8 +499,6 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
#ifdef ERTS_ENABLE_LOCK_CHECK
lc_locks = erts_proc_lc_my_proc_locks(rp);
rp_locks |= lc_locks;
- if (receiver == c_p->common.id)
- c_p_locks |= lc_locks;
#endif
if (ERTS_FORCE_ENIF_SEND_DELAY() || msgq ||
rp_locks & ERTS_PROC_LOCK_MSGQ ||
@@ -532,19 +527,25 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
msgq->last = &mp->next;
erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE);
}
+ goto done;
} else {
erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE);
rp_locks &= ~ERTS_PROC_LOCK_TRACE;
rp_locks |= ERTS_PROC_LOCK_MSGQ;
- erts_queue_message(rp, &rp_locks, mp, msg);
}
}
-#endif
+#endif /* ERTS_SMP */
+
+ erts_queue_message(rp, rp_locks, mp, msg,
+ c_p ? c_p->common.id : am_undefined);
+#ifdef ERTS_SMP
+done:
if (c_p == rp)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
if (rp_locks & ~lc_locks)
erts_smp_proc_unlock(rp, rp_locks & ~lc_locks);
+#endif
if (!scheduler)
erts_proc_dec_refc(rp);
if (flush_me) {
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index bc32f3f167..36b0af0c1a 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -1139,7 +1139,7 @@ reply_sched_wall_time(void *vswtrp)
hpp = &hp;
}
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (swtrp->req_sched == esdp->no)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -1218,7 +1218,7 @@ reply_system_check(void *vscrp)
hpp = &hp;
msg = STORE_NC(hpp, ohp, scrp->ref);
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (scrp->req_sched == esdp->no)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -10024,7 +10024,7 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result)
ASSERT(hp_start + hsz == hp);
#endif
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, c_p->common.id);
if (c_p == rp)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -11766,7 +11766,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp,
mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp);
mess = copy_struct(exit_term, term_size, &hp, ohp);
#endif
- erts_queue_message(to, to_locksp, mp, mess);
+ erts_queue_message(to, *to_locksp, mp, mess, am_system);
} else {
Eterm temp_token;
Uint sz_token;
@@ -11787,7 +11787,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp,
seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, to);
temp_token = copy_struct(token, sz_token, &hp, ohp);
ERL_MESSAGE_TOKEN(mp) = temp_token;
- erts_queue_message(to, to_locksp, mp, mess);
+ erts_queue_message(to, *to_locksp, mp, mess, am_system);
}
}
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index 346404fe2a..fadbd704bd 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -1966,7 +1966,7 @@ send_time_offset_changed_notifications(void *new_offsetp)
*patch_refp = ref;
ASSERT(hsz == size_object(message_template));
message = copy_struct(message_template, hsz, &hp, ohp);
- erts_queue_message(rp, &rp_locks, mp, message);
+ erts_queue_message(rp, rp_locks, mp, message, am_clock_service);
}
erts_smp_proc_unlock(rp, rp_locks);
}
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 436b4aca21..1c0fc0a11f 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -238,7 +238,6 @@ write_timestamp(ErtsTraceTimeStamp *tsp, Eterm **hpp)
}
#ifdef ERTS_SMP
-#define PATCH_TS_SIZE(p) patch_ts_size(TFLGS_TS_TYPE(p))
static ERTS_INLINE Uint
patch_ts_size(int ts_type)
@@ -258,7 +257,7 @@ patch_ts_size(int ts_type)
return 0;
}
}
-#endif
+#endif /* ERTS_SMP */
/*
* Write a timestamp. The timestamp MUST be the last
@@ -394,7 +393,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,
@@ -739,7 +739,7 @@ profile_send(Eterm from, Eterm message) {
else
msg = copy_struct(message, sz, &hp, &mp->hfrag.off_heap);
- erts_queue_message(profile_p, NULL, mp, msg);
+ erts_queue_message(profile_p, 0, mp, msg, from);
}
}
@@ -787,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}
@@ -812,9 +812,32 @@ 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_trace(p, p,
+ te->match_spec, args, 2,
+ ERTS_PAM_TMP_RESULT, &return_flags);
+ if (pam_result == am_false)
+ return;
+ if (ERTS_TRACE_FLAGS(p) & F_TRACE_SILENT) {
+ erts_match_set_release_result_trace(p, pam_result);
+ return;
+ }
+ } else
+ pam_result = am_true;
+
if (is_internal_pid(to)) {
if (!erts_proc_lookup(to))
goto send_to_non_existing_process;
@@ -826,23 +849,63 @@ 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_trace(p, pam_result);
}
/* Send {trace_ts, Pid, receive, Msg, Timestamp}
* or {trace, Pid, receive, Msg}
*/
void
-trace_receive(Process *c_p, Eterm msg)
+trace_receive(Process* receiver,
+ Eterm from,
+ Eterm msg, ErtsTracingEvent* te)
{
ErtsTracerNif *tnif = NULL;
- if (is_tracer_enabled(NULL, 0, &c_p->common, &tnif,
- TRACE_FUN_E_RECEIVE, am_receive))
- send_to_tracer_nif(NULL, &c_p->common, c_p->common.id,
+ Eterm pam_result;
+
+ if (!te) {
+ te = &erts_receive_tracing[erts_active_bp_ix()];
+ if (!te->on)
+ return;
+ }
+ else ASSERT(te->on);
+
+ if (te->match_spec) {
+ Eterm args[3];
+ Uint32 return_flags;
+ if (is_pid(from)) {
+ args[0] = pid_node_name(from);
+ args[1] = from;
+ }
+ else {
+ ASSERT(is_atom(from));
+ args[0] = from; /* node name or other atom (e.g 'system') */
+ args[1] = am_undefined;
+ }
+ args[2] = msg;
+ pam_result = erts_match_set_run_trace(NULL, receiver,
+ te->match_spec, args, 3,
+ ERTS_PAM_TMP_RESULT, &return_flags);
+ if (pam_result == am_false)
+ return;
+ if (ERTS_TRACE_FLAGS(receiver) & F_TRACE_SILENT) {
+ erts_match_set_release_result_trace(NULL, pam_result);
+ return;
+ }
+ } else
+ pam_result = am_true;
+
+ if (is_tracer_enabled(NULL, 0, &receiver->common, &tnif,
+ TRACE_FUN_E_RECEIVE, am_receive)) {
+ send_to_tracer_nif(NULL, &receiver->common, receiver->common.id,
tnif, TRACE_FUN_T_RECEIVE,
- am_receive, msg, THE_NON_VALUE);
+ am_receive, msg, THE_NON_VALUE, pam_result);
+ }
+ erts_match_set_release_result_trace(NULL, pam_result);
}
int
@@ -964,7 +1027,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);
}
@@ -1202,20 +1265,14 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
may remove it, and we still want to generate a trace message */
erts_tracer_update(&pre_ms_tracer, *tracer);
tracer = &pre_ms_tracer;
- pam_result = erts_match_set_run(p, match_spec, args, arity,
- ERTS_PAM_TMP_RESULT, &return_flags);
- if (is_non_value(pam_result)) {
- erts_match_set_release_result(p);
- UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
- ERTS_TRACER_CLEAR(&pre_ms_tracer);
- return 0;
- }
+ pam_result = erts_match_set_run_trace(p, p,
+ match_spec, args, arity,
+ ERTS_PAM_TMP_RESULT, &return_flags);
}
if (tracee_flags == &meta_flags) {
/* Meta trace */
if (pam_result == am_false) {
- erts_match_set_release_result(p);
UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
ERTS_TRACER_CLEAR(&pre_ms_tracer);
return return_flags;
@@ -1223,13 +1280,12 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
} else {
/* Non-meta trace */
if (*tracee_flags & F_TRACE_SILENT) {
- erts_match_set_release_result(p);
+ erts_match_set_release_result_trace(p, pam_result);
UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
ERTS_TRACER_CLEAR(&pre_ms_tracer);
return 0;
}
if (pam_result == am_false) {
- erts_match_set_release_result(p);
UnUseTmpHeap(ERL_SUB_BIN_SIZE,p);
ERTS_TRACER_CLEAR(&pre_ms_tracer);
return return_flags;
@@ -1265,11 +1321,14 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
* Build the trace tuple and send it to the port.
*/
send_to_tracer_nif_raw(p, NULL, *tracer, *tracee_flags, p->common.id,
- tnif, TRACE_FUN_T_CALL, am_call, mfa_tuple, THE_NON_VALUE, pam_result);
- erts_match_set_release_result(p);
+ tnif, TRACE_FUN_T_CALL, am_call, mfa_tuple,
+ THE_NON_VALUE, pam_result);
- if (match_spec && tracer == &pre_ms_tracer)
- ERTS_TRACER_CLEAR(&pre_ms_tracer);
+ if (match_spec) {
+ erts_match_set_release_result_trace(p, pam_result);
+ if (tracer == &pre_ms_tracer)
+ ERTS_TRACER_CLEAR(&pre_ms_tracer);
+ }
return return_flags;
}
@@ -1290,7 +1349,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);
}
@@ -1316,7 +1375,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);
}
}
@@ -1368,7 +1427,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);
}
}
@@ -1431,7 +1490,7 @@ monitor_long_schedule_proc(Process *p, BeamInstr *in_fp, BeamInstr *out_fp, Uint
{
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = bp;
- erts_queue_message(monitor_p, NULL, mp, msg);
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
}
#endif
}
@@ -1496,7 +1555,7 @@ monitor_long_schedule_port(Port *pp, ErtsPortTaskType type, Uint time)
{
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = bp;
- erts_queue_message(monitor_p, NULL, mp, msg);
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
}
#endif
}
@@ -1571,7 +1630,7 @@ monitor_long_gc(Process *p, Uint time) {
{
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = bp;
- erts_queue_message(monitor_p, NULL, mp, msg);
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
}
#endif
}
@@ -1646,7 +1705,7 @@ monitor_large_heap(Process *p) {
{
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = bp;
- erts_queue_message(monitor_p, NULL, mp, msg);
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
}
#endif
}
@@ -1678,7 +1737,7 @@ monitor_generic(Process *p, Eterm type, Eterm spec) {
{
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = bp;
- erts_queue_message(monitor_p, NULL, mp, msg);
+ erts_queue_message(monitor_p, 0, mp, msg, am_system);
}
#endif
@@ -1748,7 +1807,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:
@@ -1767,7 +1826,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);
}
@@ -1907,7 +1966,7 @@ trace_port_receive(Port *t_p, Eterm caller, Eterm what, ...)
ASSERT(hp <= (local_heap + LOCAL_HEAP_SIZE) || orig_hp);
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);
@@ -1930,7 +1989,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)
@@ -1959,7 +2018,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);
@@ -1990,7 +2049,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 */
@@ -2430,7 +2489,7 @@ sys_msg_dispatcher_func(void *unused)
queue_proc_msg:
mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = smqp->bp;
- erts_queue_message(proc,&proc_locks,mp,smqp->msg);
+ erts_queue_message(proc,proc_locks,mp,smqp->msg,am_system);
#ifdef DEBUG_PRINTOUTS
erts_fprintf(stderr, "delivered\n");
#endif
@@ -2813,7 +2872,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) {
@@ -2833,7 +2892,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..de4beadb7e 100644
--- a/erts/emulator/beam/erl_trace.h
+++ b/erts/emulator/beam/erl_trace.h
@@ -56,6 +56,15 @@
struct binary;
+typedef struct
+{
+ int on;
+ struct binary* match_spec;
+} ErtsTracingEvent;
+
+extern ErtsTracingEvent erts_send_tracing[];
+extern ErtsTracingEvent erts_receive_tracing[];
+
/* erl_bif_trace.c */
Eterm erl_seq_trace_info(Process *p, Eterm arg1);
void erts_system_monitor_clear(Process *c_p);
@@ -91,7 +100,7 @@ void erts_send_sys_msg_proc(Eterm, Eterm, Eterm, ErlHeapFragment *);
#endif
void trace_send(Process*, Eterm, Eterm);
-void trace_receive(Process *, Eterm);
+void trace_receive(Process*, Eterm, Eterm, ErtsTracingEvent*);
Uint32 erts_call_trace(Process *p, BeamInstr mfa[], struct binary *match_spec,
Eterm* args, int local, ErtsTracer *tracer);
void erts_trace_return(Process* p, BeamInstr* fi, Eterm retval,
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 49eec44053..550db95ba8 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1483,9 +1483,19 @@ do { \
#define MatchSetGetSource(MPSP) erts_match_set_get_source(MPSP)
-extern Binary *erts_match_set_compile(Process *p, Eterm matchexpr);
+extern Binary *erts_match_set_compile(Process *p, Eterm matchexpr, Eterm MFA);
Eterm erts_match_set_lint(Process *p, Eterm matchexpr);
extern void erts_match_set_release_result(Process* p);
+ERTS_GLB_INLINE void erts_match_set_release_result_trace(Process* p, Eterm);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE
+void erts_match_set_release_result_trace(Process* p, Eterm pam_result)
+{
+ if (is_not_immed(pam_result))
+ erts_match_set_release_result(p);
+}
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
enum erts_pam_run_flags {
ERTS_PAM_TMP_RESULT=1,
@@ -1493,10 +1503,12 @@ enum erts_pam_run_flags {
ERTS_PAM_CONTIGUOUS_TUPLE=4,
ERTS_PAM_IGNORE_TRACE_SILENT=8
};
-extern Eterm erts_match_set_run(Process *p, Binary *mpsp,
- Eterm *args, int num_args,
- enum erts_pam_run_flags in_flags,
- Uint32 *return_flags);
+extern Eterm erts_match_set_run_trace(Process *p,
+ Process *self,
+ Binary *mpsp,
+ Eterm *args, int num_args,
+ enum erts_pam_run_flags in_flags,
+ Uint32 *return_flags);
extern Eterm erts_match_set_get_source(Binary *mpsp);
extern void erts_match_prog_foreach_offheap(Binary *b,
void (*)(ErlOffHeap *, void *),
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 5c2595c69d..b9f6ab04c6 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -1432,10 +1432,11 @@ finalize_force_imm_drv_call(ErtsTryImmDrvCallState *sp)
static ERTS_INLINE void
queue_port_sched_op_reply(Process *rp,
- ErtsProcLocks *rp_locksp,
+ ErtsProcLocks rp_locks,
ErtsHeapFactory* factory,
Uint32 *ref_num,
- Eterm msg)
+ Eterm msg,
+ Port* prt)
{
Eterm* hp = erts_produce_heap(factory, ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE, 0);
Eterm ref;
@@ -1448,11 +1449,12 @@ queue_port_sched_op_reply(Process *rp,
erts_factory_trim_and_close(factory, &msg, 1);
- erts_queue_message(rp, rp_locksp, factory->message, msg);
+ erts_queue_message(rp, rp_locks, factory->message, msg,
+ prt ? prt->common.id : am_undefined);
}
static void
-port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg)
+port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg, Port* prt)
{
Process *rp = erts_proc_lookup_raw(to);
if (rp) {
@@ -1478,10 +1480,11 @@ port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg)
factory.off_heap));
queue_port_sched_op_reply(rp,
- &rp_locks,
+ rp_locks,
&factory,
ref_num,
- msg_copy);
+ msg_copy,
+ prt);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -1651,7 +1654,7 @@ port_badsig(Port *prt, erts_aint32_t state, int op,
state,
sigdp->flags & ERTS_P2P_SIG_DATA_FLG_BAD_OUTPUT);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg, prt);
return ERTS_PORT_REDS_BADSIG;
} /* port_badsig */
/* bad_port_signal() will
@@ -1820,7 +1823,7 @@ port_sig_outputv(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *s
}
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, reply);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, reply, prt);
cleanup_scheduled_outputv(sigdp->u.outputv.evp,
sigdp->u.outputv.cbinp);
@@ -1928,7 +1931,7 @@ port_sig_output(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *si
}
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, reply);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, reply, prt);
cleanup_scheduled_output(sigdp->u.output.bufp);
@@ -2636,7 +2639,7 @@ port_sig_exit(Port *prt,
if (sigdp->u.exit.bp)
free_message_buffer(sigdp->u.exit.bp);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, msg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, msg, prt);
return ERTS_PORT_REDS_EXIT;
}
@@ -2829,7 +2832,7 @@ port_sig_connect(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *s
msg = am_true;
}
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, msg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, msg, prt);
return ERTS_PORT_REDS_CONNECT;
}
@@ -2912,7 +2915,7 @@ port_sig_unlink(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *si
if (op == ERTS_PROC2PORT_SIG_EXEC)
port_unlink(prt, sigdp->u.unlink.from);
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_true);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
return ERTS_PORT_REDS_UNLINK;
}
@@ -3007,7 +3010,7 @@ port_sig_link(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigd
port_link_failure(sigdp->u.link.port, sigdp->u.link.to);
}
if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_true);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt);
return ERTS_PORT_REDS_LINK;
}
@@ -3064,7 +3067,8 @@ init_ack_send_reply(Port *port, Eterm resp)
}
port_sched_op_reply(port->async_open_port->to,
port->async_open_port->ref,
- resp);
+ resp,
+ port);
erts_free(ERTS_ALC_T_PRTSD, port->async_open_port);
port->async_open_port = NULL;
@@ -3461,7 +3465,7 @@ deliver_result(Port *prt, Eterm sender, Eterm pid, Eterm res)
sz_res + 3, &hp, &ohp);
res = copy_struct(res, sz_res, &hp, ohp);
tuple = TUPLE2(hp, sender, res);
- erts_queue_message(rp, &rp_locks, mp, tuple);
+ erts_queue_message(rp, rp_locks, mp, tuple, sender);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -3562,7 +3566,7 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
trace_port_send(prt, to, tuple, 1);
ERL_MESSAGE_TOKEN(mp) = am_undefined;
- erts_queue_message(rp, &rp_locks, mp, tuple);
+ erts_queue_message(rp, rp_locks, mp, tuple, prt->common.id);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
if (!scheduler)
@@ -3734,7 +3738,7 @@ deliver_vec_message(Port* prt, /* Port */
trace_port_send(prt, to, tuple, 1);
ERL_MESSAGE_TOKEN(mp) = am_undefined;
- erts_queue_message(rp, &rp_locks, mp, tuple);
+ erts_queue_message(rp, rp_locks, mp, tuple, prt->common.id);
erts_smp_proc_unlock(rp, rp_locks);
if (!scheduler)
erts_proc_dec_refc(rp);
@@ -4388,10 +4392,11 @@ port_sig_control(Port *prt,
&factory.hp,
factory.off_heap);
queue_port_sched_op_reply(rp,
- &rp_locks,
+ rp_locks,
&factory,
sigdp->ref,
- msg);
+ msg,
+ prt);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -4402,7 +4407,7 @@ port_sig_control(Port *prt,
/* failure */
if (sigdp->caller != ERTS_INVALID_PID)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg, prt);
done:
@@ -4739,10 +4744,11 @@ port_sig_call(Port *prt,
msg = TUPLE2(hp, am_ok, msg);
queue_port_sched_op_reply(rp,
- &rp_locks,
+ rp_locks,
&factory,
sigdp->ref,
- msg);
+ msg,
+ prt);
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -4754,7 +4760,7 @@ port_sig_call(Port *prt,
}
}
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg, prt);
done:
@@ -4969,7 +4975,7 @@ port_sig_info(Port *prt,
{
ASSERT(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY);
if (op != ERTS_PROC2PORT_SIG_EXEC)
- port_sched_op_reply(sigdp->caller, sigdp->ref, am_undefined);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_undefined, prt);
else {
Eterm *hp, *hp_start;
Uint hsz;
@@ -4995,10 +5001,11 @@ port_sig_info(Port *prt,
mp->data.heap_frag = bp;
erts_factory_selfcontained_message_init(&factory, mp, hp);
queue_port_sched_op_reply(rp,
- &rp_locks,
+ rp_locks,
&factory,
sigdp->ref,
- value);
+ value,
+ prt);
}
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
@@ -5115,7 +5122,7 @@ reply_io_bytes(void *vreq)
msg = TUPLE4(hp, ref, make_small(sched_id), ein, eout);
- erts_queue_message(rp, &rp_locks, mp, msg);
+ erts_queue_message(rp, rp_locks, mp, msg, am_system);
if (req->sched_id == sched_id)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -5613,7 +5620,7 @@ void driver_report_exit(ErlDrvPort ix, int status)
trace_port_send(prt, pid, tuple, 1);
ERL_MESSAGE_TOKEN(mp) = am_undefined;
- erts_queue_message(rp, &rp_locks, mp, tuple);
+ erts_queue_message(rp, rp_locks, mp, tuple, prt->common.id);
erts_smp_proc_unlock(rp, rp_locks);
if (!scheduler)
@@ -6217,15 +6224,20 @@ driver_deliver_term(Port *prt, Eterm to, ErlDrvTermData* data, int len)
done:
if (res > 0) {
+ Eterm from = am_undefined;
mess = ESTACK_POP(stack); /* get resulting value */
erts_factory_trim_and_close(&factory, &mess, 1);
- if (prt && IS_TRACED_FL(prt, F_TRACE_SEND))
- trace_port_send(prt, to, mess, 1);
+ if (prt) {
+ if (IS_TRACED_FL(prt, F_TRACE_SEND)) {
+ trace_port_send(prt, to, mess, 1);
+ }
+ from = prt->common.id;
+ }
/* send message */
ERL_MESSAGE_TOKEN(factory.message) = am_undefined;
- erts_queue_message(rp, &rp_locks, factory.message, mess);
+ erts_queue_message(rp, rp_locks, factory.message, mess, from);
}
else if (res == -2) {
/* this clause only happens when we were requested to
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 748fba15c7..dda3ba565c 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -92,6 +92,9 @@
#define ErtsInArea(ptr,start,nbytes) \
((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
+#define ErtsContainerStruct(ptr, type, member) \
+ (type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member))
+
#if defined (__WIN32__)
# include "erl_win_sys.h"
#else
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 68006e7ef3..cedc88e5fe 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -2263,7 +2263,7 @@ static void do_send_logger_message(Eterm *hp, ErlOffHeap *ohp, ErlHeapFragment *
{
ErtsMessage *mp = erts_alloc_message(0, NULL);
mp->data.heap_frag = bp;
- erts_queue_message(p, NULL /* only used for smp build */, mp, message);
+ erts_queue_message(p, 0, mp, message, am_system);
}
#endif
}
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index 48cfafb8c5..4063cbf306 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -533,13 +533,13 @@ BIF_RETTYPE hipe_bifs_merge_term_1(BIF_ALIST_1)
BIF_RET(val);
}
-struct mfa {
+struct mfa_t {
Eterm mod;
Eterm fun;
Uint ari;
};
-static int term_to_mfa(Eterm term, struct mfa *mfa)
+static int term_to_mfa(Eterm term, struct mfa_t *mfa)
{
Eterm mod, fun, a;
Uint ari;
@@ -597,7 +597,7 @@ static Uint *hipe_find_emu_address(Eterm mod, Eterm name, unsigned int arity)
Uint *hipe_bifs_find_pc_from_mfa(Eterm term)
{
- struct mfa mfa;
+ struct mfa_t mfa;
if (!term_to_mfa(term, &mfa))
return NULL;
@@ -617,7 +617,7 @@ BIF_RETTYPE hipe_bifs_set_native_address_3(BIF_ALIST_3)
Eterm *pc;
void *address;
int is_closure;
- struct mfa mfa;
+ struct mfa_t mfa;
switch (BIF_ARG_3) {
case am_false:
@@ -1225,7 +1225,7 @@ void hipe_mfa_set_trampoline(Eterm m, Eterm f, unsigned int arity, void *trampol
BIF_RETTYPE hipe_bifs_set_funinfo_native_address_3(BIF_ALIST_3)
{
- struct mfa mfa;
+ struct mfa_t mfa;
void *address;
int is_exported;
@@ -1247,7 +1247,7 @@ BIF_RETTYPE hipe_bifs_set_funinfo_native_address_3(BIF_ALIST_3)
BIF_RETTYPE hipe_bifs_invalidate_funinfo_native_addresses_1(BIF_ALIST_1)
{
Eterm lst;
- struct mfa mfa;
+ struct mfa_t mfa;
struct hipe_mfa_info *p;
hipe_mfa_info_table_rwlock();
@@ -1416,7 +1416,7 @@ BIF_RETTYPE hipe_find_na_or_make_stub(BIF_ALIST_3)
BIF_RETTYPE hipe_bifs_find_na_or_make_stub_2(BIF_ALIST_2)
{
- struct mfa mfa;
+ struct mfa_t mfa;
void *address;
int is_remote;
@@ -1518,9 +1518,9 @@ struct ref {
*/
BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIST_2)
{
- struct mfa callee;
+ struct mfa_t callee;
Eterm *tuple;
- struct mfa caller;
+ struct mfa_t caller;
void *address;
void *trampoline;
unsigned int flags;
@@ -1597,7 +1597,7 @@ BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIST_2)
*/
BIF_RETTYPE hipe_bifs_mark_referred_from_1(BIF_ALIST_1) /* get_refs_from */
{
- struct mfa mfa;
+ struct mfa_t mfa;
const struct hipe_mfa_info *p;
struct ref *ref;
@@ -1649,7 +1649,7 @@ static void hipe_purge_all_refs(void)
BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
{
- struct mfa mfa;
+ struct mfa_t mfa;
struct hipe_mfa_info *caller_mfa, *callee_mfa;
struct hipe_mfa_info_list *refers_to, *tmp_refers_to;
struct ref **prev, *ref;
@@ -1703,7 +1703,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
*/
BIF_RETTYPE hipe_bifs_redirect_referred_from_1(BIF_ALIST_1)
{
- struct mfa mfa;
+ struct mfa_t mfa;
struct hipe_mfa_info *p;
struct ref **prev, *ref;
int is_remote, res;
diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c
index 00b572029a..9c03b3811c 100644
--- a/erts/emulator/hipe/hipe_native_bif.c
+++ b/erts/emulator/hipe/hipe_native_bif.c
@@ -601,7 +601,7 @@ void hipe_clear_timeout(Process *c_p)
}
#endif
if (IS_TRACED_FL(c_p, F_TRACE_RECEIVE)) {
- trace_receive(c_p, am_timeout);
+ trace_receive(c_p, am_clock_service, am_timeout, NULL);
}
c_p->flags &= ~F_TIMO;
JOIN_MESSAGE(c_p);
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index 29e043dd5c..da6a6bdea4 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -84,7 +84,6 @@ cpu_timestamp(Config) when is_list(Config) ->
receive_trace(Config) when is_list(Config) ->
Receiver = fun_spawn(fun receiver/0),
- process_flag(trap_exit, true),
%% Trace the process; make sure that we receive the trace messages.
1 = erlang:trace(Receiver, true, ['receive']),
@@ -96,15 +95,116 @@ receive_trace(Config) when is_list(Config) ->
{trace, Receiver, 'receive', Hello2} = receive_first_trace(),
receive_nothing(),
+ %% Test 'receive' with matchspec
+ F1 = fun ({Pat, IsMatching}) ->
+ set_trace_pattern('receive', Pat, []),
+ Receiver ! Hello,
+ case IsMatching of
+ true ->
+ {trace, Receiver, 'receive', Hello} = receive_first_trace();
+ false ->
+ ok
+ end,
+ receive_nothing()
+ end,
+ From = self(),
+ Node = node(),
+ lists:foreach(F1, [{no, true},
+ {[{[Node, undefined,"Unexpected"],[],[]}], false},
+ {[{[Node, From,'_'],[],[]}], true},
+ {[{[Node, '$1','_'],[{'=/=','$1',From}],[]}], false},
+ {[{['$1', '_','_'],[{'=:=','$1',Node}],[]}], true},
+ {false, false},
+ {true, true}]),
+
+ %% Remote messages
+ OtherName = atom_to_list(?MODULE)++"_receive_trace",
+ {ok, OtherNode} = start_node(OtherName),
+ RemoteProc = spawn_link(OtherNode, ?MODULE, process, [self()]),
+ io:format("RemoteProc = ~p ~n", [RemoteProc]),
+
+ RemoteProc ! {send_please, Receiver, Hello},
+ {trace, Receiver, 'receive', Hello} = receive_first_trace(),
+ RemoteProc ! {send_please, Receiver, 99},
+ {trace, Receiver, 'receive', 99} = receive_first_trace(),
+
+ %% Remote with matchspec
+ F2 = fun (To, {Pat, IsMatching}) ->
+ set_trace_pattern('receive', Pat, []),
+ RemoteProc ! {send_please, To, Hello},
+ case IsMatching of
+ true ->
+ {trace, Receiver, 'receive', Hello} = receive_first_trace();
+ false ->
+ ok
+ end,
+ receive_nothing()
+ end,
+ F2(Receiver, {no, true}),
+ F2(Receiver, {[{[OtherNode, undefined,"Unexpected"],[],[]}], false}),
+ F2(Receiver, {[{[OtherNode, RemoteProc,'_'],[],[]},
+ {[OtherNode, undefined,'_'],[],[]}], true}),
+ F2(Receiver, {[{[OtherNode, '$1','_'],
+ [{'orelse',{'=:=','$1',undefined},{'=/=',{node,'$1'},{node}}}],
+ []}], true}),
+ F2(Receiver, {[{['$1', '_','_'], [{'=:=','$1',OtherNode}], []}], true}),
+ F2(Receiver, {false, false}),
+ F2(Receiver, {true, true}),
+
+ %% Remote to named with matchspec
+ Name = trace_SUITE_receiver,
+ register(Name, Receiver),
+ NN = {Name, node()},
+ F2(NN, {no, true}),
+ F2(NN, {[{[OtherNode, undefined,"Unexpected"],[],[]}], false}),
+ F2(NN, {[{[OtherNode, RemoteProc,'_'],[],[]},
+ {[OtherNode, undefined,'_'],[],[]}], true}),
+ F2(NN, {[{[OtherNode, '$1','_'],
+ [{'orelse',{'=:=','$1',undefined},{'=/=',{node,'$1'},{node}}}],
+ []}], true}),
+ F2(NN, {[{['$1', '_','_'], [{'==','$1',OtherNode}], []}], true}),
+ F2(NN, {false, false}),
+ F2(NN, {true, true}),
+
+ unlink(RemoteProc),
+ true = stop_node(OtherNode),
+
+ %% Timeout
+ Receiver ! {set_timeout, 10},
+ {trace, Receiver, 'receive', {set_timeout, 10}} = receive_first_trace(),
+ {trace, Receiver, 'receive', timeout} = receive_first_trace(),
+ erlang:trace_pattern('receive', [{[clock_service,undefined,timeout], [], []}], []),
+ Receiver ! {set_timeout, 7},
+ {trace, Receiver, 'receive', timeout} = receive_first_trace(),
+ erlang:trace_pattern('receive', true, []),
+
%% Another process should not be able to trace Receiver.
+ process_flag(trap_exit, true),
Intruder = fun_spawn(fun() -> erlang:trace(Receiver, true, ['receive']) end),
{'EXIT', Intruder, {badarg, _}} = receive_first(),
%% Untrace the process; we should not receive anything.
- 1 = erlang:trace(Receiver, false, ['receive']),
- Receiver ! {hello, there},
- Receiver ! any_garbage,
- receive_nothing(),
+ ?line 1 = erlang:trace(Receiver, false, ['receive']),
+ ?line Receiver ! {hello, there},
+ ?line Receiver ! any_garbage,
+ ?line receive_nothing(),
+
+ %% Verify restrictions in matchspec for 'receive'
+ F3 = fun (Pat) -> {'EXIT', {badarg,_}} = (catch erlang:trace_pattern('receive', Pat, [])) end,
+ WC = ['_','_','_'],
+ F3([{WC,[],[{message, {process_dump}}]}]),
+ F3([{WC,[{is_seq_trace}],[]}]),
+ F3([{WC,[],[{set_seq_token,label,4711}]}]),
+ F3([{WC,[],[{get_seq_token}]}]),
+ F3([{WC,[],[{enable_trace,call}]}]),
+ F3([{WC,[],[{enable_trace,self(),call}]}]),
+ F3([{WC,[],[{disable_trace,call}]}]),
+ F3([{WC,[],[{disable_trace,self(),call}]}]),
+ F3([{WC,[],[{trace,[call],[]}]}]),
+ F3([{WC,[],[{trace,self(),[],[call]}]}]),
+ F3([{WC,[],[{caller}]}]),
+ F3([{WC,[],[{silent,true}]}]),
+
ok.
%% Tests that receive of a message always happens before a call with
@@ -257,30 +357,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),
@@ -301,8 +482,26 @@ 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, [])),
+ {'EXIT',{badarg,_}} = (catch erlang:trace_pattern(send, [{['_','_'],[],[{caller}]}], [])),
+
+ %% Done.
ok.
+set_trace_pattern(_, no, _) -> 0;
+set_trace_pattern(MFA, Pat, Flg) ->
+ R = erlang:trace_pattern(MFA, Pat, Flg),
+ {match_spec, Pat} = erlang:trace_info(MFA, match_spec),
+ R.
+
%% Test trace(Pid, How, [procs]).
procs_trace(Config) when is_list(Config) ->
Name = list_to_atom(atom_to_list(?MODULE)++"_procs_trace"),
@@ -438,6 +637,7 @@ dist_procs_trace(Config) when is_list(Config) ->
Proc2 ! {unlink_please, Proc1},
{trace, Proc1, getting_unlinked, Proc2} = receive_first_trace(),
receive_nothing(),
+
%%
%% exit (with registered name, due to link)
Name = list_to_atom(OtherName),
@@ -1516,8 +1716,8 @@ receive_nothing() ->
receive
Any ->
ct:fail({unexpected_message, Any})
- after 200 ->
- ok
+ after 100 ->
+ ok
end.
@@ -1592,9 +1792,14 @@ sender() ->
%% Just consumes messages from its message queue.
receiver() ->
- receive
- _Any -> receiver()
- end.
+ receiver(infinity).
+
+receiver(Timeout) ->
+ receiver(receive
+ {set_timeout, NewTimeout} -> NewTimeout;
+ _Any -> Timeout
+ after Timeout -> infinity %% reset
+ end).
%% Works as long as it receives CPU time. Will always be RUNNABLE.
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 20a64e81b4..90fd536b15 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -1749,16 +1749,16 @@ trace_delivered(_Tracee) ->
erlang:nif_error(undefined).
%% trace_info/2
--spec erlang:trace_info(PidPortOrFunc, Item) -> Res when
- PidPortOrFunc :: pid() | port() | new | new_processes | new_ports
- | {Module, Function, Arity} | on_load,
+-spec erlang:trace_info(PidPortFuncEvent, Item) -> Res when
+ PidPortFuncEvent :: pid() | port() | new | new_processes | new_ports
+ | {Module, Function, Arity} | on_load | send | 'receive',
Module :: module(),
Function :: atom(),
Arity :: arity(),
Item :: flags | tracer | traced | match_spec
| meta | meta_match_spec | call_count | call_time | all,
Res :: trace_info_return().
-trace_info(_PidPortOrFunc, _Item) ->
+trace_info(_PidPortFuncEvent, _Item) ->
erlang:nif_error(undefined).
%% trunc/1
@@ -2381,7 +2381,7 @@ tl(_List) ->
[{[term()] | '_' ,[term()],[term()]}].
-spec erlang:trace_pattern(MFA, MatchSpec) -> non_neg_integer() when
- MFA :: trace_pattern_mfa(),
+ MFA :: trace_pattern_mfa() | send | 'receive',
MatchSpec :: (MatchSpecList :: trace_match_spec())
| boolean()
| restart
@@ -2403,7 +2403,13 @@ trace_pattern(MFA, MatchSpec) ->
call_count |
call_time.
--spec erlang:trace_pattern(MFA, MatchSpec, FlagList) -> non_neg_integer() when
+-spec erlang:trace_pattern(send, MatchSpec, []) -> non_neg_integer() when
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean();
+ ('receive', MatchSpec, []) -> non_neg_integer() when
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean();
+ (MFA, MatchSpec, FlagList) -> non_neg_integer() when
MFA :: trace_pattern_mfa(),
MatchSpec :: (MatchSpecList :: trace_match_spec())
| boolean()
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index d5ff874206..1620f52789 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -20,6 +20,7 @@
-module(dbg).
-export([p/1,p/2,c/3,c/4,i/0,start/0,stop/0,stop_clear/0,tracer/0,
tracer/2, tracer/3, get_tracer/0, get_tracer/1, tp/2, tp/3, tp/4,
+ tpe/2, ctpe/1,
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, ltp/0, wtp/1, rtp/1,
dtp/0, dtp/1, n/1, cn/1, ln/0, h/0, h/1]).
@@ -128,7 +129,12 @@ tpl(Module, Pattern) when is_atom(Module) ->
do_tp({Module, '_', '_'}, Pattern, [local]);
tpl({_Module, _Function, _Arity} = X, Pattern) ->
do_tp(X,Pattern,[local]).
-do_tp({_Module, _Function, _Arity} = X, Pattern, Flags)
+
+tpe(Event, Pattern) when Event =:= send;
+ Event =:= 'receive' ->
+ do_tp(Event, Pattern, []).
+
+do_tp(X, Pattern, Flags)
when is_integer(Pattern);
is_atom(Pattern) ->
case ets:lookup(get_pattern_table(), Pattern) of
@@ -137,17 +143,16 @@ do_tp({_Module, _Function, _Arity} = X, Pattern, Flags)
_ ->
{error, unknown_pattern}
end;
-do_tp({Module, _Function, _Arity} = X, Pattern, Flags) when is_list(Pattern) ->
+do_tp(X, Pattern, Flags) when is_list(Pattern) ->
Nodes = req(get_nodes),
- case Module of
- '_' ->
- ok;
- M when is_atom(M) ->
+ case X of
+ {M,_,_} when is_atom(M) ->
%% Try to load M on all nodes
lists:foreach(fun(Node) ->
rpc:call(Node, M, module_info, [])
end,
- Nodes)
+ Nodes);
+ _ -> ok
end,
case lint_tp(Pattern) of
{ok,_} ->
@@ -163,9 +168,9 @@ do_tp({Module, _Function, _Arity} = X, Pattern, Flags) when is_list(Pattern) ->
end.
%% All nodes are handled the same way - also the local node if it is traced
-do_tp_on_nodes(Nodes, MFA, P, Flags) ->
+do_tp_on_nodes(Nodes, X, P, Flags) ->
lists:map(fun(Node) ->
- case rpc:call(Node,erlang,trace_pattern,[MFA,P, Flags]) of
+ case rpc:call(Node,erlang,trace_pattern,[X,P, Flags]) of
N when is_integer(N) ->
{matched, Node, N};
Else ->
@@ -210,6 +215,7 @@ ctpg(Module) when is_atom(Module) ->
do_ctp({Module, '_', '_'}, [global]);
ctpg({_Module, _Function, _Arity} = X) ->
do_ctp(X,[global]).
+
do_ctp({Module, Function, Arity},[]) ->
do_ctp({Module, Function, Arity},[global]),
do_ctp({Module, Function, Arity},[local]);
@@ -217,6 +223,11 @@ do_ctp({_Module, _Function, _Arity}=MFA,Flags) ->
Nodes = req(get_nodes),
{ok,do_tp_on_nodes(Nodes,MFA,false,Flags)}.
+ctpe(Event) when Event =:= send;
+ Event =:= 'receive' ->
+ Nodes = req(get_nodes),
+ {ok,do_tp_on_nodes(Nodes,Event,true,[])}.
+
%%
%% ltp() -> ok
%% List saved and built-in trace patterns.
diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl
index 0777503ef7..5a3c885571 100644
--- a/lib/runtime_tools/test/dbg_SUITE.erl
+++ b/lib/runtime_tools/test/dbg_SUITE.erl
@@ -22,6 +22,7 @@
%% Test functions
-export([all/0, suite/0,
big/1, tiny/1, simple/1, message/1, distributed/1, port/1,
+ send/1, recv/1,
ip_port/1, file_port/1, file_port2/1, file_port_schedfix/1,
ip_port_busy/1, wrap_port/1, wrap_port_time/1,
with_seq_trace/1, dead_suspend/1, local_trace/1,
@@ -39,6 +40,7 @@ suite() ->
all() ->
[big, tiny, simple, message, distributed, port, ip_port,
+ send, recv,
file_port, file_port2, file_port_schedfix, ip_port_busy,
wrap_port, wrap_port_time, with_seq_trace, dead_suspend,
local_trace, saved_patterns, tracer_exit_on_stop,
@@ -151,6 +153,226 @@ message(Config) when is_list(Config) ->
{trace,S,call,{dbg,ln,[]},S}] = flush(),
ok.
+send(Config) when is_list(Config) ->
+ {ok, _} = start(),
+ Node = start_slave(),
+ rpc:call(Node, code, add_patha,
+ [filename:join(proplists:get_value(data_dir, Config), "..")]),
+ try
+ Echo = fun F() ->
+ receive {From, M} ->
+ From ! M,
+ F()
+ end
+ end,
+ Rcvr = spawn_link(Echo),
+ RemoteRcvr = spawn_link(Node, Echo),
+
+ {ok, [{matched, _, 1}]} = dbg:p(Rcvr, send),
+
+ send_test(Rcvr, make_ref(), true),
+
+ %% Test that the test case process is the receiving process
+ send_test(Rcvr, [{[self(),'_'],[],[]}]),
+
+ %% Test that self() is not the receiving process
+ {ok, [{matched, _node, 1}, {saved, 2}]} =
+ dbg:tpe(send, [{['$1','_'],[{'==','$1',{self}}],[]}]),
+ send_test(Rcvr, make_ref(), false),
+
+ %% Test that self() is the sending process
+ send_test(Rcvr, [{['_','_'],[{'==',Rcvr,{self}}],[]}]),
+
+ %% Test attaching a message
+ send_test(Rcvr, [{['_','_'],[{'==',Rcvr,{self}}],[{message, hello}]}],
+ make_ref(), hello),
+
+ %% Test using a saved trace pattern
+ send_test(Rcvr, 2, make_ref(), false),
+
+ %% Test clearing of trace pattern
+ {ok, [{matched, _node, 1}]} = dbg:ctpe(send),
+ send_test(Rcvr, make_ref(), true),
+
+ %% Test complex message inspection
+ Ref = make_ref(),
+ send_test(Rcvr,
+ [{['_','$2'],[{'==',Ref,{element,1,{element,2,'$2'}}}],[]}],
+ {test, {Ref}, <<0:(8*1024)>>}, true),
+
+ send_test(Rcvr, {test, {make_ref()}, <<0:(8*1024)>>}, false),
+
+ %% Test send to remote process
+ remote_send_test(Rcvr, RemoteRcvr, [], make_ref(), true),
+
+ remote_send_test(Rcvr, RemoteRcvr,
+ [{['$1','_'],[{'==',{node, '$1'},{node}}],[]}],
+ make_ref(), false),
+
+ remote_send_test(Rcvr, RemoteRcvr,
+ [{['$1','_'],[{'==',{node, '$1'},Node}],[]}],
+ make_ref(), true),
+
+ %% Test that distributed dbg works
+ dbg:tracer(Node, process, {fun myhandler/2, self()}),
+ Rcvr2 = spawn_link(Echo),
+ RemoteRcvr2 = spawn_link(Node, Echo),
+ dbg:p(Rcvr2, [send]),
+ dbg:p(RemoteRcvr2, [send]),
+ dbg:tpe(send, [{['_', hej],[],[]}]),
+
+ send_test(Rcvr2, make_ref(), false),
+ send_test(RemoteRcvr2, make_ref(), false),
+ send_test(Rcvr2, hej, true),
+ send_test(RemoteRcvr2, hej, true),
+
+ ok
+
+ after
+ stop()
+ end.
+
+send_test(Pid, Pattern, Msg, TraceEvent) ->
+ {ok, [{matched, _, _}, _]} = dbg:tpe(send, Pattern),
+ send_test(Pid, Msg, TraceEvent).
+send_test(Pid, Pattern) ->
+ send_test(Pid, Pattern, make_ref(), true).
+send_test(Pid, Msg, TraceEvent) ->
+ S = self(),
+ Pid ! {S, Msg},
+ receive Msg -> ok end,
+ send_test_rcv(Pid, Msg, S, TraceEvent).
+
+remote_send_test(Pid, RPid, Pattern, Msg, TraceEvent) ->
+ dbg:tpe(send, Pattern),
+ TMsg = {self(), Msg},
+ Pid ! {RPid, TMsg},
+ receive Msg -> ok end,
+ send_test_rcv(Pid, TMsg, RPid, TraceEvent).
+
+send_test_rcv(Pid, Msg, S, TraceEvent) ->
+ case flush() of
+ [] when not TraceEvent ->
+ ok;
+ [{trace, Pid, send, Msg, S}] when TraceEvent ->
+ ok;
+ [{trace, Pid, send, Msg, S, Message}] when TraceEvent == Message ->
+ ok;
+ Else ->
+ ct:fail({got_unexpected_message, Else})
+ end.
+
+recv(Config) when is_list(Config) ->
+ {ok, _} = start(),
+ Node = start_slave(),
+ rpc:call(Node, code, add_patha,
+ [filename:join(proplists:get_value(data_dir, Config), "..")]),
+ try
+ Echo = fun F() ->
+ receive {From, M} ->
+ From ! M,
+ F()
+ end
+ end,
+ Rcvr = spawn_link(Echo),
+ RemoteRcvr = spawn_link(Node, Echo),
+
+ {ok, [{matched, _, 1}]} = dbg:p(Rcvr, 'receive'),
+
+ recv_test(Rcvr, make_ref(), true),
+
+ %% Test that the test case process is the sending process
+ recv_test(Rcvr, [{[node(), self(), '_'],[],[]}]),
+
+ %% Test that self() is the not sending process
+ {ok, [{matched, _node, 1}, {saved, 2}]} =
+ dbg:tpe('receive', [{[node(), '$1','_'],[{'==','$1',{self}}],[]}]),
+ recv_test(Rcvr, make_ref(), false),
+
+ %% Test that self() is the receiving process
+ recv_test(Rcvr, [{'_',[{'==',Rcvr,{self}}],[]}]),
+
+ %% Test attaching a message
+ recv_test(Rcvr, [{'_',[{'==',Rcvr,{self}}],[{message, hello}]}],
+ make_ref(), hello),
+
+ %% Test using a saved trace pattern
+ recv_test(Rcvr, 2, make_ref(), false),
+
+ %% Test clearing of trace pattern
+ {ok, [{matched, _node, 1}]} = dbg:ctpe('receive'),
+ recv_test(Rcvr, make_ref(), true),
+
+ %% Test complex message inspection
+ Ref = make_ref(),
+ recv_test(Rcvr,
+ [{[node(), '_','$2'],[{'==',Ref,{element,1,
+ {element,2,
+ {element,2,'$2'}}}}],[]}],
+ {test, {Ref}, <<0:(8*1024)>>}, true),
+
+ recv_test(Rcvr, {test, {make_ref()}, <<0:(8*1024)>>}, false),
+
+ %% Test recv to remote process
+ remote_recv_test(RemoteRcvr, Rcvr, [], make_ref(), true),
+
+ remote_recv_test(RemoteRcvr, Rcvr,
+ [{['$1',undefined,'_'],[{'==','$1',{node}}],[]}],
+ make_ref(), false),
+
+ remote_recv_test(RemoteRcvr, Rcvr,
+ [{['$1',undefined,'_'],[{'==','$1',Node}],[]}],
+ make_ref(), true),
+
+ %% Test that distributed dbg works
+ dbg:tracer(Node, process, {fun myhandler/2, self()}),
+ Rcvr2 = spawn_link(Echo),
+ RemoteRcvr2 = spawn_link(Node, Echo),
+ dbg:p(Rcvr2, ['receive']),
+ dbg:p(RemoteRcvr2, ['receive']),
+ dbg:tpe('receive', [{[node(), '_', '$1'],[{'==',{element,2,'$1'}, hej}],[]}]),
+
+ recv_test(Rcvr2, make_ref(), false),
+ recv_test(RemoteRcvr2, make_ref(), false),
+ recv_test(Rcvr2, hej, true),
+ recv_test(RemoteRcvr2, hej, true),
+
+ ok
+
+ after
+ stop()
+ end.
+
+recv_test(Pid, Pattern, Msg, TraceEvent) ->
+ {ok, [{matched, _, _}, _]} = dbg:tpe('receive', Pattern),
+ recv_test(Pid, Msg, TraceEvent).
+recv_test(Pid, Pattern) ->
+ recv_test(Pid, Pattern, make_ref(), true).
+recv_test(Pid, Msg, TraceEvent) ->
+ S = self(),
+ Pid ! {S, Msg},
+ receive Msg -> ok end,
+ recv_test_rcv(Pid, {S, Msg}, TraceEvent).
+
+remote_recv_test(RPid, Pid, Pattern, Msg, TraceEvent) ->
+ dbg:tpe('receive', Pattern),
+ TMsg = {self(), Msg},
+ RPid ! {Pid, TMsg},
+ receive Msg -> ok end,
+ recv_test_rcv(Pid, TMsg, TraceEvent).
+
+recv_test_rcv(Pid, Msg, TraceEvent) ->
+ case flush() of
+ [] when not TraceEvent ->
+ ok;
+ [{trace, Pid, 'receive', Msg}] when TraceEvent ->
+ ok;
+ [{trace, Pid, 'receive', Msg, Message}] when TraceEvent == Message ->
+ ok;
+ Else ->
+ ct:fail({got_unexpected_message, Else})
+ end.
+
%% Simple test of distributed tracing
distributed(Config) when is_list(Config) ->
{ok, _} = start(),