aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorLukas Larsson <[email protected]>2016-04-15 15:10:55 +0200
committerLukas Larsson <[email protected]>2016-04-15 15:10:55 +0200
commitbe7f01689e71cbc1896ea5c11fb0ebc6f7f6dd8a (patch)
treee7db1292fe6aa167408b7a7c93289fe2cca7e5c1 /lib
parent67df3b3792857a2b4885c0acbeaa7f32f3594b0c (diff)
parentcff38617986001e0a5f3f48de20acbeccceea978 (diff)
downloadotp-be7f01689e71cbc1896ea5c11fb0ebc6f7f6dd8a.tar.gz
otp-be7f01689e71cbc1896ea5c11fb0ebc6f7f6dd8a.tar.bz2
otp-be7f01689e71cbc1896ea5c11fb0ebc6f7f6dd8a.zip
Merge branch 'lukas/erts/tracer-module-and-more/PR-1009/OTP-10268'
* lukas/erts/tracer-module-and-more/PR-1009/OTP-10268: (26 commits) erts: Don't trace on link events when port is dead erts: more logging in trace_bif_SUITE trace_bif_return erts: Make trace_delivered go via sys msg dispatcher again erts: Add comment about future trace optimizations erts: Optimize tracer reload test erts: Document erlang:match_spec_test/3 runtime_tools: Make dbg work with erl_tracer modules runtime_rools: Allow new timestamp trace flags runtime_tools: Lots of dbg docs updates erts: Deallocate heap fragments from trace nif calls eprof: Fix tests after tracer module incompatabilities fprof: update to work with new spawned trace event erts: Add 'spawned' trace event to 'procs' trace flag erts: send and receive no longer need status lock erts: Do 'unregister' as "self-tracing" erts: Silence harmless valgrind warning in dec_term erts: Fix end_per_testcase crash in local_trace_SUITE observer: Update ttb to work with tracing on ports runtime_tools: Update dbg to work with tracing on ports erts: Add tracing examples in match spec docs ...
Diffstat (limited to 'lib')
-rw-r--r--lib/kernel/doc/src/seq_trace.xml11
-rw-r--r--lib/kernel/src/seq_trace.erl16
-rw-r--r--lib/kernel/test/seq_trace_SUITE.erl3
-rw-r--r--lib/observer/src/ttb.erl4
-rw-r--r--lib/runtime_tools/c_src/Makefile.in69
-rw-r--r--lib/runtime_tools/c_src/trace_file_drv.c13
-rw-r--r--lib/runtime_tools/doc/src/dbg.xml292
-rw-r--r--lib/runtime_tools/src/dbg.erl93
-rw-r--r--lib/runtime_tools/test/dbg_SUITE.erl112
-rw-r--r--lib/runtime_tools/test/dbg_SUITE_data/Makefile.src8
-rw-r--r--lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c113
-rw-r--r--lib/sasl/src/systools_make.erl2
-rw-r--r--lib/stdlib/doc/src/ets.xml2
-rw-r--r--lib/tools/src/fprof.erl15
-rw-r--r--lib/tools/test/eprof_SUITE.erl3
-rw-r--r--lib/tools/test/fprof_SUITE.erl27
16 files changed, 556 insertions, 227 deletions
diff --git a/lib/kernel/doc/src/seq_trace.xml b/lib/kernel/doc/src/seq_trace.xml
index 1feb3fcb07..5ac199b6a7 100644
--- a/lib/kernel/doc/src/seq_trace.xml
+++ b/lib/kernel/doc/src/seq_trace.xml
@@ -213,9 +213,10 @@ seq_trace:set_token(OldToken), % activate the trace token again
<type name="tracer"/>
<desc>
<p>Sets the system tracer. The system tracer can be either a
- process or port denoted by <c><anno>Tracer</anno></c>. Returns the previous
- value (which can be <c>false</c> if no system tracer is
- active).</p>
+ process, port or <seealso marker="erts:erl_tracer">tracer module</seealso>
+ denoted by <c><anno>Tracer</anno></c>.
+ Returns the previous value (which can be <c>false</c> if no system
+ tracer is active).</p>
<p>Failure: <c>{badarg, Info}}</c> if <c><anno>Pid</anno></c> is not an
existing local pid.</p>
</desc>
@@ -225,7 +226,7 @@ seq_trace:set_token(OldToken), % activate the trace token again
<fsummary>Return the pid() or port() of the current system tracer.</fsummary>
<type name="tracer"/>
<desc>
- <p>Returns the pid or port identifier of the current system
+ <p>Returns the pid, port identifier or tracer module of the current system
tracer or <c>false</c> if no system tracer is activated.</p>
</desc>
</func>
@@ -298,7 +299,7 @@ TimeStamp = {Seconds, Milliseconds, Microseconds}
matches a message in a receive statement, according to the trace
token carried by the received message, empty or not.</p>
<p>On each Erlang node, a process can be set as the <em>system tracer</em>.
- This process receives trace messages each time
+ This process will receive trace messages each time
a message with a trace token is sent or received (if the trace
token flag <c>send</c> or <c>'receive'</c> is set). The system
tracer can then print each trace event, write it to a file, or
diff --git a/lib/kernel/src/seq_trace.erl b/lib/kernel/src/seq_trace.erl
index 01ac024c88..cc0c10909b 100644
--- a/lib/kernel/src/seq_trace.erl
+++ b/lib/kernel/src/seq_trace.erl
@@ -106,14 +106,24 @@ reset_trace() ->
%% reset_trace(Pid) -> % this might be a useful function too
--type tracer() :: (Pid :: pid()) | port() | 'false'.
+-type tracer() :: (Pid :: pid()) | port() |
+ (TracerModule :: {module(), term()}) |
+ 'false'.
-spec set_system_tracer(Tracer) -> OldTracer when
Tracer :: tracer(),
OldTracer :: tracer().
-set_system_tracer(Pid) ->
- erlang:system_flag(sequential_tracer, Pid).
+set_system_tracer({Module, State} = Tracer) ->
+ case erlang:module_loaded(Module) of
+ false ->
+ Module:enabled(trace_status, erlang:self(), State);
+ true ->
+ ok
+ end,
+ erlang:system_flag(sequential_tracer, Tracer);
+set_system_tracer(Tracer) ->
+ erlang:system_flag(sequential_tracer, Tracer).
-spec get_system_tracer() -> Tracer when
Tracer :: tracer().
diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl
index 0a03503163..000994751f 100644
--- a/lib/kernel/test/seq_trace_SUITE.erl
+++ b/lib/kernel/test/seq_trace_SUITE.erl
@@ -787,7 +787,6 @@ start_tracer() ->
seq_trace:set_system_tracer(Pid),
Pid.
-
set_token_flags([]) ->
ok;
set_token_flags([no_timestamp|Flags]) ->
@@ -836,7 +835,7 @@ check_ts(strict_monotonic_timestamp, Ts) ->
ct:fail({unexpected_timestamp, Ts})
end,
ok.
-
+
start_node(Name, Param) ->
test_server:start_node(Name, slave, [{args, Param}]).
diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl
index c60c0385b8..4d6eb3ba8d 100644
--- a/lib/observer/src/ttb.erl
+++ b/lib/observer/src/ttb.erl
@@ -434,7 +434,9 @@ procs(Procs) when is_list(Procs) ->
procs(Proc) ->
proc(Proc).
-proc(Procs) when Procs=:=all; Procs=:=existing; Procs=:=new ->
+proc(Procs) when Procs=:=all; Procs=:=ports; Procs=:=processes;
+ Procs=:=existing; Procs=:=existing_ports; Procs=:=existing_processes;
+ Procs=:=new; Procs=:=new_ports; Procs=:=new_processes ->
[Procs];
proc(Name) when is_atom(Name) ->
[Name]; % can be registered on this node or other node
diff --git a/lib/runtime_tools/c_src/Makefile.in b/lib/runtime_tools/c_src/Makefile.in
index 58c12e0d88..70b48daf97 100644
--- a/lib/runtime_tools/c_src/Makefile.in
+++ b/lib/runtime_tools/c_src/Makefile.in
@@ -33,21 +33,18 @@ VSN=$(RUNTIME_TOOLS_VSN)
# be set for that system only.
# ----------------------------------------------------
CC = $(DED_CC)
-CFLAGS = $(DED_CFLAGS)
+CFLAGS = $(DED_CFLAGS) -I./
LD = $(DED_LD)
SHELL = /bin/sh
LIBS = $(DED_LIBS)
LDFLAGS += $(DED_LDFLAGS)
-DTRACE_LIBNAME = dyntrace
+TRACE_LIBNAME = dyntrace trace_file_drv trace_ip_drv
SYSINCLUDE = $(DED_SYS_INCLUDE)
TRACE_DRV_INCLUDES = $(SYSINCLUDE)
-ALL_CFLAGS = $(CFLAGS) @DEFS@ $(TYPE_FLAGS) $(TRACE_DRV_INCLUDES) \
- -I$(OBJDIR) -I$(ERL_TOP)/erts/emulator/$(TARGET)
-
ifeq ($(TYPE),debug)
TYPEMARKER = .debug
TYPE_FLAGS = $(subst -O3,,$(subst -O2,,$(CFLAGS))) -DDEBUG @DEBUG_FLAGS@
@@ -61,6 +58,9 @@ TYPE_FLAGS = $(CFLAGS)
endif
endif
+ALL_CFLAGS = @DEFS@ $(TYPE_FLAGS) $(TRACE_DRV_INCLUDES) \
+ -I$(OBJDIR) -I$(ERL_TOP)/erts/emulator/$(TARGET)
+
ROOTDIR = $(ERL_TOP)/lib
PRIVDIR = ../priv
LIBDIR = $(PRIVDIR)/lib/$(TARGET)
@@ -74,37 +74,16 @@ RELSYSDIR = $(RELEASE_PATH)/lib/runtime_tools-$(VSN)
# ----------------------------------------------------
# Misc Macros
# ----------------------------------------------------
-before_DTrace_OBJS = $(OBJDIR)/dyntrace$(TYPEMARKER).o
-## NIF_MAKEFILE = $(PRIVDIR)/Makefile
-# Higher-level makefiles says that we can only compile on UNIX flavors
-NIF_LIB = $(LIBDIR)/dyntrace$(TYPEMARKER).@DED_EXT@
+TRACE_LIBS = $(foreach LIB, $(TRACE_LIBNAME), $(LIBDIR)/$(LIB)$(TYPEMARKER).@DED_EXT@)
-ifeq ($(HOST_OS),)
-HOST_OS := $(shell $(ERL_TOP)/erts/autoconf/config.guess)
-endif
-
-TRACE_IP_DRV_OBJS = \
- $(OBJDIR)/trace_ip_drv.o
-
-TRACE_FILE_DRV_OBJS = \
- $(OBJDIR)/trace_file_drv.o
-
-ifeq ($(findstring win32,$(TARGET)), win32)
-SOLIBS = $(LIBDIR)/trace_ip_drv.dll $(LIBDIR)/trace_file_drv.dll
-LN=cp
-else
-SOLIBS = $(LIBDIR)/trace_ip_drv.so $(LIBDIR)/trace_file_drv.so
-endif
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
_create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR))
-debug opt valgrind: $(SOLIBS) $(OBJDIR) $(LIBDIR) $(NIF_LIB)
-
-DYNTRACE_OBJS = $(before_DTrace_OBJS)
+debug opt valgrind: $(SOLIBS) $(OBJDIR) $(LIBDIR) $(TRACE_LIBS)
$(OBJDIR):
-@mkdir -p $(OBJDIR)
@@ -112,50 +91,26 @@ $(OBJDIR):
$(LIBDIR):
-@mkdir -p $(LIBDIR)
-$(OBJDIR)/dyntrace$(TYPEMARKER).o: dyntrace.c
- $(V_at)$(INSTALL_DIR) $(OBJDIR)
- $(V_CC) -c -o $@ $(ALL_CFLAGS) $<
-
-$(NIF_LIB): $(DYNTRACE_OBJS)
- $(V_at)$(INSTALL_DIR) $(LIBDIR)
- $(V_LD) $(LDFLAGS) -o $@ $^ $(LDLIBS)
-
-$(OBJDIR)/%.o: %.c
+$(OBJDIR)/%$(TYPEMARKER).o: %.c
$(V_CC) -c -o $@ $(ALL_CFLAGS) $<
-$(LIBDIR)/trace_ip_drv.so: $(TRACE_IP_DRV_OBJS)
- $(V_LD) $(LDFLAGS) -o $@ $^ -lc $(LIBS)
-
-$(LIBDIR)/trace_file_drv.so: $(TRACE_FILE_DRV_OBJS)
- $(V_LD) $(LDFLAGS) -o $@ $^ -lc $(LIBS)
-
-$(LIBDIR)/trace_ip_drv.dll: $(TRACE_IP_DRV_OBJS)
- $(V_LD) $(LDFLAGS) -o $@ $^ $(LIBS)
-$(LIBDIR)/trace_file_drv.dll: $(TRACE_FILE_DRV_OBJS)
+$(LIBDIR)/%$(TYPEMARKER).@DED_EXT@: $(OBJDIR)/%$(TYPEMARKER).o
$(V_LD) $(LDFLAGS) -o $@ $^ $(LIBS)
clean:
- rm -f $(SOLIBS) $(TRACE_IP_DRV_OBJS) $(TRACE_FILE_DRV_OBJS)
- rm -f $(LIBDIR)/dyntrace.@DED_EXT@
- rm -f $(LIBDIR)/dyntrace.debug.@DED_EXT@
- rm -f $(LIBDIR)/dyntrace.valgrind.@DED_EXT@
- rm -f $(OBJDIR)/dyntrace.o
- rm -f $(OBJDIR)/dyntrace.debug.o
- rm -f $(OBJDIR)/dyntrace.valgrind.o
+ rm -f $(TRACE_LIBS)
rm -f core *~
docs:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) "$(RELSYSDIR)/priv/obj"
$(INSTALL_DIR) "$(RELSYSDIR)/priv/lib"
- $(INSTALL_PROGRAM) $(DYNTRACE_OBJS) "$(RELSYSDIR)/priv/obj"
- $(INSTALL_PROGRAM) $(NIF_LIB) $(SOLIBS) "$(RELSYSDIR)/priv/lib"
+ $(INSTALL_PROGRAM) $(TRACE_LIBS) "$(RELSYSDIR)/priv/lib"
release_docs_spec:
diff --git a/lib/runtime_tools/c_src/trace_file_drv.c b/lib/runtime_tools/c_src/trace_file_drv.c
index 703766c188..e7fd5968c1 100644
--- a/lib/runtime_tools/c_src/trace_file_drv.c
+++ b/lib/runtime_tools/c_src/trace_file_drv.c
@@ -172,6 +172,7 @@ static ErlDrvData trace_file_start(ErlDrvPort port, char *buff);
static void trace_file_stop(ErlDrvData handle);
static void trace_file_output(ErlDrvData handle, char *buff,
ErlDrvSizeT bufflen);
+static void trace_file_outputv(ErlDrvData handle, ErlIOVec *ev);
static void trace_file_finish(void);
static ErlDrvSSizeT trace_file_control(ErlDrvData handle,
unsigned int command,
@@ -214,7 +215,7 @@ ErlDrvEntry trace_file_driver_entry = {
NULL, /* void * that is not used (BC) */
trace_file_control, /* F_PTR control, port_control callback */
trace_file_timeout, /* F_PTR timeout, driver_set_timer callback */
- NULL, /* F_PTR outputv, reserved */
+ trace_file_outputv, /* F_PTR outputv, reserved */
NULL, /* ready_async */
NULL, /* flush */
NULL, /* call */
@@ -363,6 +364,16 @@ static void trace_file_stop(ErlDrvData handle)
/*
** Data sent from erlang to port.
*/
+static void trace_file_outputv(ErlDrvData handle, ErlIOVec *ev)
+{
+ int i;
+ for (i = 0; i < ev->vsize; i++) {
+ if (ev->iov[i].iov_len)
+ trace_file_output(handle, ev->iov[i].iov_base,
+ ev->iov[i].iov_len);
+ }
+}
+
static void trace_file_output(ErlDrvData handle, char *buff,
ErlDrvSizeT bufflen)
{
diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml
index 0232cc0453..0128e23a47 100644
--- a/lib/runtime_tools/doc/src/dbg.xml
+++ b/lib/runtime_tools/doc/src/dbg.xml
@@ -36,14 +36,30 @@
<modulesummary>The Text Based Trace Facility</modulesummary>
<description>
<p>This module implements a text based interface to the
- <c>trace/3</c> and the <c>trace_pattern/2</c> BIFs. It makes it
- possible to trace functions, processes and messages on text based
- terminals. It can be used instead of, or as complement to, the
- <c>pman</c> module.
- </p>
- <p>For some examples of how to use <c>dbg</c> from the Erlang
+ <c><seealso marker="erts:erlang#trace-3">trace/3</seealso></c> and the
+ <c><seealso marker="erts:erlang#trace_pattern-2">trace_pattern/2</seealso></c> BIFs. It makes it
+ possible to trace functions, processes, ports and messages.
+ </p>
+ <p>
+ To quickly get started on tracing function calls you can use the following
+ code in the Erlang shell:
+ </p>
+ <pre>
+1> dbg:tracer(). %% Start the default trace message receiver
+{ok,&lt;0.36.0>}
+2> dbg:p(all, c). %% Setup call (c) tracing on all processes
+{ok,[{matched,nonode@nohost,26}]}
+3> dbg:tp(lists, seq, x). %% Setup an exception return trace (x) on lists:seq
+{ok,[{matched,nonode@nohost,2},{saved,x}]}
+4> lists:seq(1,10).
+(&lt;0.34.0>) call lists:seq(1,10)
+(&lt;0.34.0>) returned from lists:seq/2 -> [1,2,3,4,5,6,7,8,9,10]
+[1,2,3,4,5,6,7,8,9,10]
+ </pre>
+ <p>
+ For more examples of how to use <c>dbg</c> from the Erlang
shell, see the <seealso marker="#simple_example">simple example</seealso> section.
- </p>
+ </p>
<p>The utilities are also suitable to use in system testing on
large systems, where other tools have too much impact on the
system performance. Some primitive support for sequential tracing
@@ -164,53 +180,66 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<desc>
<p>Traces <c>Item</c> in accordance to the value specified
by <c>Flags</c>. The variation of <c>Item</c> is listed below:</p>
- <list type="bulleted">
- <item>If the <c>Item</c> is a <c>pid()</c>, the corresponding
- process is traced. The process may be a remote process
- (on another Erlang node). The node must be in the list of
- traced nodes (see <seealso marker="#n"><c>n/1</c></seealso> and
- <c>tracer/0/2/3</c>).</item>
- <item>If the <c>Item</c> is the atom <c>all</c>, all processes in the
- system as well as all processes created hereafter are
- to be traced. This also affects all nodes added with the
- <c>n/1</c> or <c>tracer/0/2/3</c> function.</item>
- <item>If the <c>Item</c> is the atom <c>new</c>, no currently existing
- processes are affected, but every process created after the
- call is.This also affects all nodes added with the
- <c>n/1</c> or <c>tracer/0/2/3</c> function.</item>
- <item>If the <c>Item</c> is the atom <c>existing</c>, all
- existing processes are traced, but new processes will not
- be affected.This also affects all nodes added with the
- <c>n/1</c> or <c>tracer/0/2/3</c> function.</item>
- <item>If the <c>Item</c> is an atom other than <c>all</c>,
- <c>new</c> or <c>existing</c>, the process with the
- corresponding registered name is traced.The process may be a
- remote process (on another Erlang node). The node must be added
- with the <c>n/1</c> or <c>tracer/0/2/3</c> function.</item>
- <item>If the <c>Item</c> is an integer, the process <c><![CDATA[<0.Item.0>]]></c> is
- traced.</item>
- <item>If the <c>Item</c> is a tuple <c>{X, Y, Z}</c>, the
- process <c><![CDATA[<X.Y.Z>]]></c> is
- traced. </item>
+ <taglist>
+ <tag><c>pid()</c> or <c>port()</c></tag>
+ <item>The corresponding process or port is traced. The process or port may
+ be a remote process or port (on another Erlang node). The node must
+ be in the list of traced nodes (see <seealso marker="#n-1"><c>n/1</c></seealso>
+ and <c><seealso marker="#tracer-3">tracer/3</seealso></c>).</item>
+ <tag><c>all</c></tag>
+ <item>All processes and ports in the system as well as all processes and ports
+ created hereafter are to be traced.</item>
+ <tag><c>all_processes</c></tag>
+ <item>All processes in the system as well as all processes created hereafter are to be traced.</item>
+ <tag><c>all_ports</c></tag>
+ <item>All ports in the system as well as all ports created hereafter are to be traced.</item>
+ <tag><c>new</c></tag>
+ <item>All processes and ports created after the call is are to be traced.</item>
+ <tag><c>new_processes</c></tag>
+ <item>All processes created after the call is are to be traced.</item>
+ <tag><c>new_ports</c></tag>
+ <item>All ports created after the call is are to be traced.</item>
+ <tag><c>existing</c></tag>
+ <item>All existing processes and ports are traced.</item>
+ <tag><c>existing_processes</c></tag>
+ <item>All existing processes are traced.</item>
+ <tag><c>existing_ports</c></tag>
+ <item>All existing ports are traced.</item>
+ <tag><c>atom()</c></tag>
+ <item>The process or port with the corresponding registered name is traced. The process or
+ port may be a remote process (on another Erlang node). The node must be
+ added with the <c><seealso marker="#n-1">n/1</seealso></c> or
+ <c><seealso marker="#tracer-3">tracer/3</seealso></c> function.</item>
+ <tag><c>integer()</c></tag>
+ <item>The process <c><![CDATA[<0.Item.0>]]></c> is traced.</item>
+ <tag><c>{X, Y, Z}</c></tag>
+ <item>The process <c><![CDATA[<X.Y.Z>]]></c> is traced. </item>
+ <tag><c>string()</c></tag>
<item>If the <c>Item</c> is a string <![CDATA["<X.Y.Z>"]]>
- as returned from <c>pid_to_list/1</c>, the process
+ as returned from <c><seealso marker="erts:erlang#pid_to_list-1">pid_to_list/1</seealso></c>, the process
<c><![CDATA[<X.Y.Z>]]></c> is traced. </item>
- </list>
+ </taglist>
+
+ <p>When enabling an <c>Item</c> that represents a group of processes,
+ the <c>Item</c> is enabled on all nodes added with the
+ <c><seealso marker="#n-1">n/1</seealso></c> or
+ <c><seealso marker="#tracer-3">tracer/3</seealso></c> function.</p>
+
<p><c>Flags</c> can be a single atom,
or a list of flags. The available flags are:
</p>
<taglist>
<tag><c>s (send)</c></tag>
<item>
- <p>Traces the messages the process sends.</p>
+ <p>Traces the messages the process or port sends.</p>
</item>
<tag><c>r (receive)</c></tag>
<item>
- <p>Traces the messages the process receives.</p>
+ <p>Traces the messages the process or port receives.</p>
</item>
<tag><c>m (messages)</c></tag>
<item>
- <p>Traces the messages the process receives and sends.</p>
+ <p>Traces the messages the process or port receives and sends.</p>
</item>
<tag><c>c (call)</c></tag>
<item>
@@ -221,6 +250,10 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<item>
<p>Traces process related events to the process.</p>
</item>
+ <tag><c>ports</c></tag>
+ <item>
+ <p>Traces port related events to the port.</p>
+ </item>
<tag><c>sos (set on spawn)</c></tag>
<item>
<p>Lets all processes created by the traced
@@ -241,8 +274,8 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<tag><c>sofl (set on first link)</c></tag>
<item>
<p>This is the same as <c>sol</c>, but only for
- the first call to
- <c>link/1</c> by the traced process.</p>
+ the first call to
+ <c><seealso marker="erts:erlang#link-1">link/1</seealso></c> by the traced process.</p>
</item>
<tag><c>all</c></tag>
<item>
@@ -255,10 +288,10 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
</item>
</taglist>
<p>The list can also include any of the flags allowed in
- <c>erlang:trace/3</c></p>
+ <c><seealso marker="erts:erlang#trace-3">erlang:trace/3</seealso></c></p>
<p>The function returns either an error tuple or a tuple
<c>{ok, List}</c>. The <c>List</c> consists of
- specifications of how many processes that matched (in the
+ specifications of how many processes and ports that matched (in the
case of a pure pid() exactly 1). The specification of
matched processes is <c>{matched, Node, N}</c>. If the
remote processor call,<c>rpc</c>, to a remote node fails,
@@ -286,9 +319,9 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
</func>
<func>
<name>i() -> ok</name>
- <fsummary>Display information about all traced processes.</fsummary>
+ <fsummary>Display information about all traced processes and ports.</fsummary>
<desc>
- <p>Displays information about all traced processes.</p>
+ <p>Displays information about all traced processes and ports.</p>
</desc>
</func>
<func>
@@ -327,35 +360,41 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
</type>
<desc>
<p>This function enables call trace for one or more
- functions. All exported functions matching the <c>{Module, Function, Arity}</c> argument will be concerned, but the
+ functions. All exported functions matching the <c>{Module, Function, Arity}</c>
+ argument will be concerned, but the
<c>match_spec()</c> may further narrow down the set of function
calls generating trace messages.</p>
<p>For a description of the <c>match_spec()</c> syntax,
please turn to the
<em>User's guide</em> part of the online
documentation for the runtime system (<em>erts</em>). The
- chapter <em>Match Specification in Erlang</em> explains the
- general match specification "language".</p>
+ chapter <em><seealso marker="erts:match_spec">Match Specifications in Erlang</seealso></em>
+ explains the general match specification "language".
+ The most common generic match specifications used can be
+ found as <c>Built-inAlias</c>', see
+ <c><seealso marker="#ltp-0">ltp/0</seealso></c> below for details.
+ </p>
<p>The Module, Function and/or Arity parts of the tuple may
be specified as the atom <c>'_'</c> which is a "wild-card"
matching all modules/functions/arities. Note, if the
Module is specified as <c>'_'</c>, the Function and Arity
parts have to be specified as '_' too. The same holds for the
Functions relation to the Arity.</p>
- <p>All nodes added with <c>n/1</c> or <c>tracer/0/2/3</c> will
+ <p>All nodes added with <c><seealso marker="#n-1">n/1</seealso></c> or
+ <c><seealso marker="#tracer-3">tracer/3</seealso></c> will
be affected by this call, and if Module is not <c>'_'</c>
the module will be loaded on all nodes.</p>
<p>The function returns either an error tuple or a tuple
<c>{ok, List}</c>. The <c>List</c> consists of specifications of how
- many functions that matched, in the same way as the processes
- are presented in the return value of <c>p/2</c>. </p>
+ many functions that matched, in the same way as the processes and ports
+ are presented in the return value of <c><seealso marker="#p-2">p/2</seealso></c>. </p>
<p>There may be a tuple <c>{saved, N}</c> in the return value,
if the MatchSpec is other
than []. The integer <c>N</c> may then be used in
subsequent calls to this function and will stand as an
"alias" for the given expression. There are also a couple of
- built-in aliases for common expressions, see <c>ltp/0</c> below
- for details.</p>
+ built-in aliases for common expressions, see
+ <c><seealso marker="#ltp-0">ltp/0</seealso></c> below for details.</p>
<p>If an error is returned, it can be due to errors in
compilation of the match specification. Such errors are
presented as a list of tuples <c>{error, string()}</c> where
@@ -394,7 +433,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<name>tpl({Module, Function, Arity}, MatchSpec) -> {ok, MatchDesc} | {error, term()}</name>
<fsummary>Set pattern for traced local (as well as global) function calls</fsummary>
<desc>
- <p>This function works as <c>tp/2</c>, but enables
+ <p>This function works as <c><seealso marker="#tp-2">tp/2</seealso></c>, but enables
tracing for local calls (and local functions) as well as for
global calls (and functions).</p>
</desc>
@@ -441,10 +480,10 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<p>This function disables call tracing on the specified
functions. The semantics of the parameter is the same
as for the corresponding function specification in
- <c>tp/2</c> or <c>tpl/2</c>. Both local and global call trace
+ <c><seealso marker="#tp-2">tp/2</seealso></c> or <c><seealso marker="#tpl-2">tpl/2</seealso></c>. Both local and global call trace
is disabled. </p>
<p>The return value reflects how many functions that matched,
- and is constructed as described in <c>tp/2</c>. No tuple
+ and is constructed as described in <c><seealso marker="#tp-2">tp/2</seealso></c>. No tuple
<c>{saved, N}</c> is however ever returned (for obvious reasons).</p>
</desc>
</func>
@@ -480,8 +519,8 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<name>ctpl({Module, Function, Arity}) -> {ok, MatchDesc} | {error, term()}</name>
<fsummary>Clear call trace pattern for the specified functions</fsummary>
<desc>
- <p>This function works as <c>ctp/1</c>, but only disables
- tracing set up with <c>tpl/2</c> (not with <c>tp/2</c>).</p>
+ <p>This function works as <c><seealso marker="#ctp-1">ctp/1</seealso></c>, but only disables
+ tracing set up with <c><seealso marker="#tpl-2">tpl/2</seealso></c> (not with <c><seealso marker="#tp-2">tp/2</seealso></c>).</p>
</desc>
</func>
<func>
@@ -516,8 +555,8 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<name>ctpg({Module, Function, Arity}) -> {ok, MatchDesc} | {error, term()}</name>
<fsummary>Clear call trace pattern for the specified functions</fsummary>
<desc>
- <p>This function works as <c>ctp/1</c>, but only disables
- tracing set up with <c>tp/2</c> (not with <c>tpl/2</c>).</p>
+ <p>This function works as <c><seealso marker="#ctp-1">ctp/1</seealso></c>, but only disables
+ tracing set up with <c><seealso marker="#tp-2">tp/2</seealso></c> (not with <c><seealso marker="#tpl-2">tpl/2</seealso></c>).</p>
</desc>
</func>
<func>
@@ -526,13 +565,13 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<desc>
<p>Use this function to recall all match specifications previously
used in the session (i. e. previously saved during calls
- to <c>tp/2</c>, and built-in match specifications.
+ to <c><seealso marker="#tp-2">tp/2</seealso></c>, and built-in match specifications.
This is very useful, as a complicated
match_spec can be quite awkward to write. Note that the
- match specifications are lost if <c>stop/0</c> is called.</p>
+ match specifications are lost if <c><seealso marker="#stop-0">stop/0</seealso></c> is called.</p>
<p>Match specifications used can be saved in a file (if a
read-write file system is present) for use in later
- debugging sessions, see <c>wtp/1</c> and <c>rtp/1</c></p>
+ debugging sessions, see <c><seealso marker="#wtp-1">wtp/1</seealso></c> and <c><seealso marker="#rtp-1">rtp/1</seealso></c></p>
<p>There are three built-in trace patterns:
<c>exception_trace</c>, <c>caller_trace</c>
and <c>caller_exception_trace</c> (or <c>x</c>, <c>c</c> and
@@ -555,10 +594,10 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<fsummary>Delete all saved match specifications.</fsummary>
<desc>
<p>Use this function to "forget" all match specifications
- saved during calls to <c>tp/2</c>.
+ saved during calls to <c><seealso marker="#tp-2">tp/2</seealso></c>.
This is useful when one wants to restore other match
- specifications from a file with <c>rtp/1</c>. Use
- <c>dtp/1</c> to delete specific saved match specifications. </p>
+ specifications from a file with <c><seealso marker="#rtp-1">rtp/1</seealso></c>. Use
+ <c><seealso marker="#dtp-1">dtp/1</seealso></c> to delete specific saved match specifications. </p>
</desc>
</func>
<func>
@@ -569,7 +608,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
</type>
<desc>
<p>Use this function to "forget" a specific match specification
- saved during calls to <c>tp/2</c>.</p>
+ saved during calls to <c><seealso marker="#tp-2">tp/2</seealso></c>.</p>
</desc>
</func>
<func>
@@ -581,12 +620,12 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
</type>
<desc>
<p>This function will save all match specifications saved
- during the session (during calls to <c>tp/2</c>)
+ during the session (during calls to <c><seealso marker="#tp-2">tp/2</seealso></c>)
and built-in match specifications in a text
file with the name designated by <c>Name</c>. The format
of the file is textual, why it can be edited with an
ordinary text editor, and then restored with
- <c>rtp/1</c>. </p>
+ <c><seealso marker="#rtp-1">rtp/1</seealso></c>. </p>
<p>Each match spec in the file ends with a full stop
(<c>.</c>) and new (syntactically correct) match
specifications can be added to the file manually.</p>
@@ -604,7 +643,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
</type>
<desc>
<p>This function reads match specifications from a file
- (possibly) generated by the <c>wtp/1</c> function. It checks
+ (possibly) generated by the <c><seealso marker="#wtp-1">wtp/1</seealso></c> function. It checks
the syntax of all match specifications and verifies that
they are correct. The error handling principle is "all or
nothing", i. e. if some of the match specifications are
@@ -612,14 +651,14 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
saved match specifications for the running system. </p>
<p>The match specifications in the file are <em>merged</em>
with the current match specifications, so that no duplicates
- are generated. Use <c>ltp/0</c> to see what numbers were
+ are generated. Use <c><seealso marker="#ltp-0">ltp/0</seealso></c> to see what numbers were
assigned to the specifications from the file.</p>
<p>The function will return an error, either due to I/O
problems (like a non existing or non readable file) or due
to file format problems. The errors from a bad format file
are in a more or less textual format, which will give a hint
- to what's causing the problem. <marker id="n"></marker>
-</p>
+ to what's causing the problem.
+ </p>
</desc>
</func>
<func>
@@ -631,12 +670,12 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
</type>
<desc>
<p>The <c>dbg</c> server keeps a list of nodes where tracing
- should be performed. Whenever a <c>tp/2</c> call or a
- <c>p/2</c> call is made, it is executed for all nodes in this
- list including the local node (except for <c>p/2</c> with a
- specific <c>pid()</c> as first argument, in which case the
+ should be performed. Whenever a <c><seealso marker="#tp-2">tp/2</seealso></c> call or a
+ <c><seealso marker="#p-2">p/2</seealso></c> call is made, it is executed for all nodes in this
+ list including the local node (except for <c><seealso marker="#p-2">p/2</seealso></c> with a
+ specific <c>pid()</c> or <c>port()</c> as first argument, in which case the
command is executed only on the node where the designated
- process resides).
+ process or port resides).
</p>
<p>This function adds a remote node (<c>Nodename</c>) to the
list of nodes where tracing is performed. It starts a tracer
@@ -645,17 +684,17 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
distribution). If no tracer process is running on the local
node, the error reason <c>no_local_tracer</c> is returned. The
tracer process on the local node must be started with the
- <c>tracer/0/2</c> function.
+ <c><seealso marker="#tracer-2">tracer/0/2</seealso></c> function.
</p>
<p>If <c>Nodename</c> is the local node, the error reason
<c>cant_add_local_node</c> is returned.
</p>
- <p>If a trace port (see <seealso marker="#trace_port"><c>trace_port/2</c></seealso>) is
+ <p>If a trace port (see <seealso marker="#trace_port-2"><c>trace_port/2</c></seealso>) is
running on the local node, remote nodes can not be traced with
a tracer process. The error reason
<c>cant_trace_remote_pid_to_local_port</c> is returned. A
trace port can however be started on the remote node with the
- <c>tracer/3</c> function.
+ <c><seealso marker="#tracer-3">tracer/3</seealso></c> function.
</p>
<p>The function will also return an error if the node
<c>Nodename</c> is not reachable.</p>
@@ -669,7 +708,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
</type>
<desc>
<p>Clears a node from the list of traced nodes. Subsequent
- calls to <c>tp/2</c> and <c>p/2</c> will not consider that
+ calls to <c><seealso marker="#tp-2">tp/2</seealso></c> and <c><seealso marker="#p-2">p/2</seealso></c> will not consider that
node, but tracing already activated on the node will continue
to be in effect.</p>
<p>Returns <c>ok</c>, cannot fail.</p>
@@ -688,37 +727,42 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<desc>
<p>This function starts a server on the local node that will
be the recipient of all trace messages. All subsequent calls
- to <c>p/2</c> will result in messages sent to the newly
+ to <c><seealso marker="#p-2">p/2</seealso></c> will result in messages sent to the newly
started trace server.</p>
<p>A trace server started in this way will simply display the
trace messages in a formatted way in the Erlang shell
- (i. e. use io:format). See <c>tracer/2</c> for a description
- of how the trace message handler can be customized. <marker id="tracer2"></marker>
-</p>
- <p>To start a similar tracer on a remote node, use <c>n/1</c>.</p>
+ (i. e. use io:format). See <c><seealso marker="#tracer-2">tracer/2</seealso></c> for a description
+ of how the trace message handler can be customized.
+ </p>
+ <p>To start a similar tracer on a remote node, use <c><seealso marker="#n-1">n/1</seealso></c>.</p>
</desc>
</func>
<func>
<name>tracer(Type, Data) -> {ok, pid()} | {error, Error}</name>
<fsummary>Start a tracer server with additional parameters</fsummary>
<type>
- <v>Type = port | process</v>
- <v>Data = PortGenerator | HandlerSpec</v>
- <v>HandlerSpec = {HandlerFun, InitialData}</v>
- <v>HandlerFun = fun() (two arguments)</v>
- <v>InitialData = term()</v>
+ <v>Type = port | process | module</v>
+ <v>Data = PortGenerator | HandlerSpec | ModuleSpec</v>
<v>PortGenerator = fun() (no arguments)</v>
<v>Error = term()</v>
+ <v>HandlerSpec = {HandlerFun, InitialData}</v>
+ <v>HandlerFun = fun() (two arguments)</v>
+ <v>ModuleSpec = fun() (no arguments) | {TracerModule, TracerState}</v>
+ <v>ModuleModule = atom()</v>
+ <v>InitialData = TracerState = term()</v>
</type>
<desc>
<p>This function starts a tracer server with additional
parameters on the local node. The first parameter, the
<c>Type</c>, indicates if trace messages should be handled
- by a receiving process (<c>process</c>) or by a tracer port
- (<c>port</c>). For a description about tracer ports see
- <c>trace_port/2</c>.
+ by a receiving process (<c>process</c>), by a tracer port
+ (<c>port</c>) or by a tracer module
+ (<c>module</c>). For a description about tracer ports see
+ <c><seealso marker="#trace_port-2">trace_port/2</seealso></c>
+ and for a tracer modules see
+ <c><seealso marker="erts:erl_tracer">erl_tracer</seealso>.</c>
</p>
- <p>If <c>Type</c> is a process, a message handler function can
+ <p>If <c>Type</c> is <c>process</c>, a message handler function can
be specified (<c>HandlerSpec</c>). The handler function, which
should be a <c>fun</c> taking two arguments, will be called
for each trace message, with the first argument containing the
@@ -729,18 +773,22 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
choose any appropriate action to take when invoked, and can
save a state for the next invocation by returning it.
</p>
- <p>If <c>Type</c> is a port, then the second parameter should
+ <p>If <c>Type</c> is <c>port</c>, then the second parameter should
be a <em>fun</em> which takes no arguments and returns a
newly opened trace port when called. Such a <em>fun</em> is
- preferably generated by calling <c>trace_port/2</c>.
+ preferably generated by calling <c><seealso marker="#trace_port-2">trace_port/2</seealso></c>.
</p>
+ <p>if <c>Type</c> is <c>module</c>, then the second parameter should
+ be either a tuple describing the <c><seealso marker="erts:erl_tracer">erl_tracer</seealso></c>
+ module to be used for tracing and the state to be used for
+ that tracer module or a fun returning the same tuple.</p>
<p>If an error is returned, it can either be due to a tracer
server already running (<c>{error,already_started}</c>) or
due to the <c>HandlerFun</c> throwing an exception.
</p>
<p>To start a similar tracer on a remote node, use
- <c>tracer/3</c>. <marker id="trace_port"></marker>
-</p>
+ <c><seealso marker="#tracer-3">tracer/3</seealso></c>.
+ </p>
</desc>
</func>
<func>
@@ -750,20 +798,19 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<v>Nodename = atom()</v>
</type>
<desc>
- <p>This function is equivalent to <c>tracer/2</c>, but acts on
+ <p>This function is equivalent to <c><seealso marker="#tracer-2">tracer/2</seealso></c>, but acts on
the given node. A tracer is started on the node
- (<c>Nodename</c>) and the node is added to the list of traced
- nodes.
+ (<c>Nodename</c>) and the node is added to the list of traced nodes.
</p>
<note>
- <p>This function is not equivalent to <c>n/1</c>. While
- <c>n/1</c> starts a process tracer which redirects all trace
+ <p>This function is not equivalent to <c><seealso marker="#n-1">n/1</seealso></c>. While
+ <c><seealso marker="#n-1">n/1</seealso></c> starts a process tracer which redirects all trace
information to a process tracer on the local node (i.e. the
- trace control node), <c>tracer/3</c> starts a tracer of any
+ trace control node), <c><seealso marker="#tracer-3">tracer/3</seealso></c> starts a tracer of any
type which is independent of the tracer on the trace control
node.</p>
</note>
- <p>For details, see <seealso marker="#tracer2"><c>tracer/2</c></seealso>.</p>
+ <p>For details, see <c><seealso marker="#tracer-2">tracer/2</seealso></c>.</p>
</desc>
</func>
<func>
@@ -795,9 +842,9 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<c>file</c> and the <c>ip</c> trace drivers. The file driver
sends all trace messages into one or several binary files,
from where they later can be fetched and processed with the
- <c>trace_client/2</c> function. The ip driver opens a TCP/IP
+ <c><seealso marker="#trace_client-2">trace_client/2</seealso></c> function. The ip driver opens a TCP/IP
port where it listens for connections. When a client
- (preferably started by calling <c>trace_client/2</c> on
+ (preferably started by calling <c><seealso marker="#trace_client-2">trace_client/2</seealso></c> on
another Erlang node) connects, all trace messages are sent
over the IP network for further processing by the remote
client. </p>
@@ -836,7 +883,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
as fast as they are produced by the runtime system, a special
message is sent, which indicates how many messages that are
dropped. That message will arrive at the handler function
- specified in <c>trace_client/3</c> as the tuple <c>{drop, N}</c> where <c>N</c> is the number of consecutive messages
+ specified in <c><seealso marker="#trace_client-3">trace_client/3</seealso></c> as the tuple <c>{drop, N}</c> where <c>N</c> is the number of consecutive messages
dropped. In case of heavy tracing, drop's are likely to occur,
and they surely occur if no client is reading the trace
messages.</p>
@@ -890,7 +937,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
</item>
<tag><c>get_listen_port</c></tag>
<item>
- <p>Returns <c>{ok, IpPort}</c> where <c>IpPort</c>is
+ <p>Returns <c>{ok, IpPort}</c> where <c>IpPort</c> is
the IP port number used by the driver listen socket.
Only the ip trace driver supports this operation.</p>
</item>
@@ -913,7 +960,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<desc>
<p>This function starts a trace client that reads the output
created by a trace port driver and handles it in mostly the
- same way as a tracer process created by the <c>tracer/0</c>
+ same way as a tracer process created by the <c><seealso marker="#tracer-0">tracer/0</seealso></c>
function.</p>
<p>If <c>Type</c> is <c>file</c>, the client reads all trace
messages stored in the file named <c>Filename</c> or
@@ -925,7 +972,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<p>If <c>Type</c> is <c>follow_file</c>, the client behaves as
in the <c>file</c> case, but keeps trying to read (and
process) more data
- from the file until stopped by <c>stop_trace_client/1</c>.
+ from the file until stopped by <c><seealso marker="#stop_trace_client-1">stop_trace_client/1</seealso></c>.
<c>WrapFilesSpec</c> is not allowed as second argument
for this <c>Type</c>.</p>
<p>If <c>Type</c> is <c>ip</c>, the client connects to the
@@ -981,10 +1028,10 @@ hello</pre>
<v>InitialData = term()</v>
</type>
<desc>
- <p>This function works exactly as <c>trace_client/2</c>, but
+ <p>This function works exactly as <c><seealso marker="#trace_client-2">trace_client/2</seealso></c>, but
allows you to write your own handler function. The handler
function works mostly as the one described in
- <c>tracer/2</c>, but will also have to be prepared to handle
+ <c><seealso marker="#tracer-2">tracer/2</seealso></c>, but will also have to be prepared to handle
trace messages of the form <c>{drop, N}</c>, where <c>N</c> is
the number of dropped messages. This pseudo trace message will
only occur if the ip trace driver is used.</p>
@@ -1003,7 +1050,7 @@ hello</pre>
<desc>
<p>This function shuts down a previously started trace
client. The <c>Pid</c> argument is the process id returned
- from the <c>trace_client/2</c> or <c>trace_client/3</c> call.</p>
+ from the <c><seealso marker="#trace_client-2">trace_client/2</seealso></c> or <c><seealso marker="#trace_client-3">trace_client/3</seealso></c> call.</p>
</desc>
</func>
<func>
@@ -1018,11 +1065,11 @@ hello</pre>
<fsummary>Return the process or port to which all trace messages are sent.</fsummary>
<type>
<v>Nodename = atom()</v>
- <v>Tracer = port() | pid()</v>
+ <v>Tracer = port() | pid() | {module(), term()}</v>
</type>
<desc>
- <p>Returns the process or port to which all trace
- messages are sent. </p>
+ <p>Returns the process, port or tracer module to which all trace
+ messages are sent.</p>
</desc>
</func>
<func>
@@ -1156,8 +1203,9 @@ SeqTrace [0]: (&lt;0.30.0>) &lt;0.25.0> ! {dbg,{ok,&lt;0.31.0>}} [Serial: {4,5}]
of causing a deadlock. This will happen if a group leader process generates a trace
message and the tracer process, by calling the trace handler function, sends an IO
request to the same group leader. The problem can only occur if the trace handler
- prints to tty using an <c>io</c> function such as <c>format/2</c>. Note that when
- <c>dbg:p(all,call)</c> is called, IO processes are also traced.
+ prints to tty using an <c>io</c> function such as <c><seealso marker="stdlib:io#format-2">format/2</seealso></c>.
+ Note that when
+ <c>dbg:p(all,call)</c> is called, IO processes are also traced.
Here's an example:</p>
<pre>
%% Using a default line editing shell
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index 6eea1a0917..d5ff874206 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -298,7 +298,12 @@ tracer(port, Port) when is_port(Port) ->
start(fun() -> Port end);
tracer(process, {Handler,HandlerData}) ->
- start(fun() -> start_tracer_process(Handler, HandlerData) end).
+ start(fun() -> start_tracer_process(Handler, HandlerData) end);
+
+tracer(module, Fun) when is_function(Fun) ->
+ start(Fun);
+tracer(module, {Module, State}) ->
+ start(fun() -> {Module, State} end).
remote_tracer(port, Fun) when is_function(Fun) ->
@@ -308,7 +313,13 @@ remote_tracer(port, Port) when is_port(Port) ->
remote_start(fun() -> Port end);
remote_tracer(process, {Handler,HandlerData}) ->
- remote_start(fun() -> start_tracer_process(Handler, HandlerData) end).
+ remote_start(fun() -> start_tracer_process(Handler, HandlerData) end);
+
+remote_tracer(module, Fun) when is_function(Fun) ->
+ remote_start(Fun);
+remote_tracer(module, {Module, State}) ->
+ remote_start(fun() -> {Module, State} end).
+
remote_start(StartTracer) ->
case (catch StartTracer()) of
@@ -543,9 +554,8 @@ c(M, F, A, Flags) ->
{error,Reason} -> {error,Reason};
Flags1 ->
tracer(),
- {ok, Tracer} = get_tracer(),
S = self(),
- Pid = spawn(fun() -> c(S, M, F, A, [{tracer, Tracer} | Flags1]) end),
+ Pid = spawn(fun() -> c(S, M, F, A, [get_tracer_flag() | Flags1]) end),
Mref = erlang:monitor(process, Pid),
receive
{'DOWN', Mref, _, _, Reason} ->
@@ -660,6 +670,9 @@ loop({C,T}=SurviveLinks, Table) ->
reply(From, {error, Reason});
Tracer when is_pid(Tracer); is_port(Tracer) ->
put(node(),{self(),Tracer}),
+ reply(From, {ok,self()});
+ {Module, _State} = Tracer when is_atom(Module) ->
+ put(node(),{self(),Tracer}),
reply(From, {ok,self()})
end;
{_Relay,_Tracer} ->
@@ -710,6 +723,9 @@ loop({C,T}=SurviveLinks, Table) ->
{_LocalRelay,Tracer} when is_port(Tracer) ->
reply(From, {error, cant_trace_remote_pid_to_local_port}),
loop(SurviveLinks, Table);
+ {_LocalRelay,Tracer} when is_tuple(Tracer) ->
+ reply(From, {error, cant_trace_remote_pid_to_local_module}),
+ loop(SurviveLinks, Table);
{_LocalRelay,Tracer} when is_pid(Tracer) ->
case (catch relay(Node, Tracer)) of
{ok,Relay} ->
@@ -879,9 +895,9 @@ trac(Proc, How, Flags) ->
end
end.
-trac(Node, {_Relay, Tracer}, AtomPid, How, Flags) ->
+trac(Node, {_Replay, Tracer}, AtomPid, How, Flags) ->
case rpc:call(Node, ?MODULE, erlang_trace,
- [AtomPid, How, [{tracer, Tracer} | Flags]]) of
+ [AtomPid, How, [get_tracer_flag(Tracer) | Flags]]) of
N when is_integer(N) ->
{matched, Node, N};
{badrpc,Reason} ->
@@ -1114,7 +1130,7 @@ transform_flags([sos|Tail],Acc) -> transform_flags(Tail,[set_on_spawn|Acc]);
transform_flags([sol|Tail],Acc) -> transform_flags(Tail,[set_on_link|Acc]);
transform_flags([sofs|Tail],Acc) -> transform_flags(Tail,[set_on_first_spawn|Acc]);
transform_flags([sofl|Tail],Acc) -> transform_flags(Tail,[set_on_first_link|Acc]);
-transform_flags([all|_],_Acc) -> all()--[silent];
+transform_flags([all|_],_Acc) -> all()--[silent,running];
transform_flags([F|Tail]=List,Acc) when is_atom(F) ->
case lists:member(F, all()) of
true -> transform_flags(Tail,[F|Acc]);
@@ -1123,9 +1139,10 @@ transform_flags([F|Tail]=List,Acc) when is_atom(F) ->
transform_flags(Bad,_Acc) -> {error,{bad_flags,Bad}}.
all() ->
- [send,'receive',call,procs,garbage_collection,running,
+ [send,'receive',call,procs,ports,garbage_collection,running,
set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link,
- timestamp,arity,return_to,silent].
+ timestamp,monotonic_timestamp,strict_monotonic_timestamp,
+ arity,return_to,silent,running_procs,running_ports].
display_info([Node|Nodes]) ->
io:format("~nNode ~w:~n",[Node]),
@@ -1146,24 +1163,34 @@ display_info1([]) ->
ok.
get_info() ->
- get_info(processes(),[]).
+ get_info(processes(),get_info(erlang:ports(),[])).
+get_info([Port|T], Acc) when is_port(Port) ->
+ case pinfo(Port, name) of
+ undefined ->
+ get_info(T,Acc);
+ {name, Name} ->
+ get_info(T,get_tinfo(Port, Name, Acc))
+ end;
get_info([Pid|T],Acc) ->
case pinfo(Pid, initial_call) of
undefined ->
get_info(T,Acc);
{initial_call, Call} ->
- case tinfo(Pid, flags) of
- undefined ->
- get_info(T,Acc);
- {flags,[]} ->
- get_info(T,Acc);
- {flags,Flags} ->
- get_info(T,[{Pid,Call,Flags}|Acc])
- end
+ get_info(T,get_tinfo(Pid, Call, Acc))
end;
get_info([],Acc) -> Acc.
+get_tinfo(P, Id, Acc) ->
+ case tinfo(P, flags) of
+ undefined ->
+ Acc;
+ {flags,[]} ->
+ Acc;
+ {flags,Flags} ->
+ [{P,Id,Flags}|Acc]
+ end.
+
format_trace([]) -> [];
format_trace([Item]) -> [ts(Item)];
format_trace([Item|T]) -> [ts(Item) ," | ", format_trace(T)].
@@ -1188,9 +1215,22 @@ to_pidspec(X) when is_pid(X) ->
true -> X;
false -> {badpid,X}
end;
-to_pidspec(new) -> new;
-to_pidspec(all) -> all;
-to_pidspec(existing) -> existing;
+to_pidspec(X) when is_port(X) ->
+ case erlang:port_info(X) of
+ undefined -> {badport, X};
+ _ -> X
+ end;
+to_pidspec(Tag)
+ when Tag =:= all;
+ Tag =:= ports;
+ Tag =:= processes;
+ Tag =:= new;
+ Tag =:= new_ports;
+ Tag =:= new_processes;
+ Tag =:= existing;
+ Tag =:= existing_ports;
+ Tag =:= existing_processes ->
+ Tag;
to_pidspec(X) when is_atom(X) ->
case whereis(X) of
undefined -> {badpid,X};
@@ -1203,6 +1243,7 @@ to_pidspec(X) -> {badpid,X}.
%%
to_pid(X) when is_pid(X) -> X;
+to_pid(X) when is_port(X) -> X;
to_pid(X) when is_integer(X) -> to_pid({0,X,0});
to_pid({X,Y,Z}) ->
to_pid(lists:concat(["<",integer_to_list(X),".",
@@ -1217,9 +1258,12 @@ to_pid(X) when is_list(X) ->
to_pid(X) -> {badpid,X}.
+pinfo(P, X) when node(P) == node(), is_port(P) -> erlang:port_info(P, X);
pinfo(P, X) when node(P) == node() -> erlang:process_info(P, X);
+pinfo(P, X) when is_port(P) -> check(rpc:call(node(P), erlang, port_info, [P, X]));
pinfo(P, X) -> check(rpc:call(node(P), erlang, process_info, [P, X])).
+
tinfo(P, X) when node(P) == node() -> erlang:trace_info(P, X);
tinfo(P, X) -> check(rpc:call(node(P), erlang, trace_info, [P, X])).
@@ -1411,6 +1455,13 @@ get_tracer() ->
req({get_tracer,node()}).
get_tracer(Node) ->
req({get_tracer,Node}).
+get_tracer_flag() ->
+ {ok, Tracer} = get_tracer(),
+ get_tracer_flag(Tracer).
+get_tracer_flag({Module,State}) ->
+ {tracer, Module, State};
+get_tracer_flag(Port = Pid) when is_port(Port); is_pid(Pid)->
+ {tracer, Pid = Port}.
save_pattern([]) ->
0;
diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl
index d40efcafef..17c04c0ed4 100644
--- a/lib/runtime_tools/test/dbg_SUITE.erl
+++ b/lib/runtime_tools/test/dbg_SUITE.erl
@@ -22,14 +22,16 @@
%% Test functions
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
- big/1, tiny/1, simple/1, message/1, distributed/1,
+ big/1, tiny/1, simple/1, message/1, distributed/1, port/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,
- saved_patterns/1, tracer_exit_on_stop/1]).
+ saved_patterns/1, tracer_exit_on_stop/1,
+ erl_tracer/1, distributed_erl_tracer/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
-export([tracee1/1, tracee2/1]).
-export([dummy/0, exported/1]).
+-export([enabled/3, trace/6, load_nif/1]).
-include_lib("common_test/include/ct.hrl").
-define(default_timeout, ?t:minutes(1)).
@@ -45,10 +47,11 @@ end_per_testcase(_Case, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [big, tiny, simple, message, distributed, ip_port,
+ [big, tiny, simple, message, distributed, port, ip_port,
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].
+ local_trace, saved_patterns, tracer_exit_on_stop,
+ erl_tracer, distributed_erl_tracer].
groups() ->
[].
@@ -272,6 +275,35 @@ local_trace(Config) when is_list(Config) ->
end,
ok.
+port(suite) ->
+ [];
+port(doc) ->
+ ["Test that tracing on port works"];
+port(Config) when is_list(Config) ->
+ try
+ S = self(),
+ start(),
+ TestFile = filename:join(proplists:get_value(priv_dir, Config),"port_test"),
+ Fun = dbg:trace_port(file, TestFile),
+
+ %% Do a run to get rid of all extra port operations
+ port_close(Fun()),
+
+ dbg:p(new,ports),
+ Port = Fun(),
+ port_close(Port),
+ stop(),
+
+ TraceFileDrv = list_to_atom(lists:flatten(["trace_file_drv n ",TestFile])),
+ [{trace,Port,open,S,TraceFileDrv},
+ {trace,Port,getting_linked,S},
+ {trace,Port,closed,normal},
+ {trace,Port,unlink,S}] = flush()
+ after
+ dbg:stop()
+ end,
+ ok.
+
saved_patterns(suite) ->
[];
saved_patterns(doc) ->
@@ -776,6 +808,78 @@ spawn_once_handler(Event, {Pid, Fun}) ->
{Pid, done}
end.
+%% Test that erl_tracer modules work correctly
+erl_tracer(Config) ->
+ stop(),
+
+ ok = load_nif(Config),
+
+ Self = self(),
+ {ok, _} = dbg:tracer(module, {?MODULE, Self}),
+ {ok, {?MODULE, Self}} = dbg:get_tracer(),
+ {ok, _} = dbg:p(self(), [c, timestamp]),
+ {ok, _} = dbg:tp(?MODULE, dummy, []),
+ ok = ?MODULE:dummy(),
+ [{Self, call, Self, Self, {?MODULE, dummy, []}, undefined, #{}}] = flush(),
+ ok.
+
+%% Test that distributed erl_tracer modules work
+distributed_erl_tracer(Config) ->
+ stop(),
+
+ S = self(),
+
+ ok = load_nif(Config),
+
+ LNode = node(),
+ RNode = start_slave(),
+ true = rpc:call(RNode, code, add_patha, [filename:join(proplists:get_value(data_dir, Config), "..")]),
+ ok = rpc:call(RNode, ?MODULE, load_nif, [Config]),
+
+ NifProxy = fun() ->
+ register(nif_proxy, self()),
+ receive M -> S ! M end
+ end,
+
+ LNifProxy = spawn_link(LNode, NifProxy),
+ RNifProxy = spawn_link(RNode, NifProxy),
+
+ TracerFun = fun() -> {?MODULE, whereis(nif_proxy)} end,
+
+ {ok, _} = dbg:tracer(LNode, module, TracerFun),
+ {ok, _} = dbg:tracer(RNode, module, TracerFun),
+
+ {ok, [{matched, _, _}, {matched, _, _}]} = dbg:p(all,c),
+ {ok, [_, _]} = dbg:tp(?MODULE, dummy, []),
+
+ {ok, {?MODULE, LNifProxy}} = dbg:get_tracer(LNode),
+ {ok, {?MODULE, RNifProxy}} = dbg:get_tracer(RNode),
+
+ LCall = spawn_link(LNode, fun() -> ?MODULE:dummy() end),
+ [{LCall, call, LNifProxy, LCall, {?MODULE, dummy, []}, undefined, #{}}] = flush(),
+
+ RCall = spawn_link(RNode, fun() -> ?MODULE:dummy() end),
+ [{RCall, call, RNifProxy, RCall, {?MODULE, dummy, []}, undefined, #{}}] = flush(),
+
+
+ ok.
+
+load_nif(Config) ->
+ SoFile = atom_to_list(?MODULE),
+ DataDir = proplists:get_value(data_dir, Config),
+ case erlang:load_nif(filename:join(DataDir, SoFile) , 0) of
+ {error, {reload, _}} ->
+ ok;
+ ok ->
+ ok
+ end.
+
+enabled(_, _, _) ->
+ erlang:nif_error(nif_not_loaded).
+
+trace(_, _, _, _, _, _) ->
+ erlang:nif_error(nif_not_loaded).
+
%%
%% Support functions
%%
diff --git a/lib/runtime_tools/test/dbg_SUITE_data/Makefile.src b/lib/runtime_tools/test/dbg_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..f3d63bd4d1
--- /dev/null
+++ b/lib/runtime_tools/test/dbg_SUITE_data/Makefile.src
@@ -0,0 +1,8 @@
+
+NIF_LIBS = dbg_SUITE@dll@
+
+all: $(NIF_LIBS)
+
+@SHLIB_RULES@
+
+$(NIF_LIBS): dbg_SUITE.c
diff --git a/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c b/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c
new file mode 100644
index 0000000000..45f14938c6
--- /dev/null
+++ b/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c
@@ -0,0 +1,113 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2009-2014. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include "erl_nif.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+
+/* NIF interface declarations */
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
+static void unload(ErlNifEnv* env, void* priv_data);
+
+/* The NIFs: */
+static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
+static ErlNifFunc nif_funcs[] = {
+ {"enabled", 3, enabled},
+ {"trace", 6, trace}
+};
+
+ERL_NIF_INIT(dbg_SUITE, nif_funcs, load, NULL, upgrade, unload)
+
+static ERL_NIF_TERM atom_trace;
+static ERL_NIF_TERM atom_ok;
+
+#define ASSERT(expr) assert(expr)
+
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+
+ atom_trace = enif_make_atom(env, "trace");
+ atom_ok = enif_make_atom(env, "ok");
+
+ *priv_data = NULL;
+
+ return 0;
+}
+
+static void unload(ErlNifEnv* env, void* priv_data)
+{
+
+}
+
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
+ ERL_NIF_TERM load_info)
+{
+ if (*old_priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+ if (*priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+ if (load(env, priv_data, load_info)) {
+ return -1;
+ }
+ return 0;
+}
+
+static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int state_arity;
+ const ERL_NIF_TERM *state_tuple;
+ ERL_NIF_TERM value;
+ ASSERT(argc == 3);
+
+
+ return atom_trace;
+}
+
+static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int state_arity;
+ ErlNifPid self, to;
+ ERL_NIF_TERM *tuple, msg;
+ ASSERT(argc == 6);
+
+ tuple = enif_alloc(sizeof(ERL_NIF_TERM)*(argc+1));
+ memcpy(tuple+1,argv,sizeof(ERL_NIF_TERM)*argc);
+
+ if (enif_self(env, &self)) {
+ tuple[0] = enif_make_pid(env, &self);
+ } else {
+ tuple[0] = enif_make_atom(env, "undefined");
+ }
+
+ msg = enif_make_tuple_from_array(env, tuple, argc + 1);
+ enif_get_local_pid(env, argv[1], &to);
+ enif_send(env, &to, NULL, msg);
+ enif_free(tuple);
+
+ return atom_ok;
+}
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index c23607f8a1..352e4984df 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -1490,7 +1490,7 @@ mandatory_modules() ->
preloaded() ->
%% Sorted
- [erl_prim_loader,erlang,
+ [erl_prim_loader,erl_tracer,erlang,
erts_code_purger,
erts_internal,init,otp_ring0,prim_eval,prim_file,
prim_inet,prim_zip,zlib].
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 4e430d101e..2d69c201bc 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -1621,6 +1621,8 @@ true</pre>
if the match_spec does not match the object <c><anno>Tuple</anno></c>.</p>
<p>This is a useful debugging and test tool, especially when
writing complicated <c>ets:select/2</c> calls.</p>
+ <p>See also: <seealso marker="erts:erlang#match_spec_test/3">
+ erlang:match_spec_test/3</seealso>.</p>
</desc>
</func>
<func>
diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl
index c5c24c8eb3..f9da748fef 100644
--- a/lib/tools/src/fprof.erl
+++ b/lib/tools/src/fprof.erl
@@ -1567,13 +1567,20 @@ trace_handler({trace_ts, Pid, return_to, {_M, _F, Args} = MFArgs, TS} = Trace,
trace_return_to(Table, Pid, Func, TS),
TS;
%%
-%% spawn
+%% spawn, only needed (and reliable) prior to 19.0
trace_handler({trace_ts, Pid, spawn, Child, MFArgs, TS} = Trace,
Table, _, Dump) ->
dump_stack(Dump, get(Pid), Trace),
trace_spawn(Table, Child, MFArgs, TS, Pid),
TS;
%%
+%% spawned, added in 19.0
+trace_handler({trace_ts, Pid, spawned, Parent, MFArgs, TS} = Trace,
+ Table, _, Dump) ->
+ dump_stack(Dump, get(Pid), Trace),
+ trace_spawn(Table, Pid, MFArgs, TS, Parent),
+ TS;
+%%
%% exit
trace_handler({trace_ts, Pid, exit, _Reason, TS} = Trace,
Table, _, Dump) ->
@@ -2014,8 +2021,10 @@ trace_spawn(Table, Pid, MFArgs, TS, Parent) ->
ets:insert(Table, #proc{id = Pid, parent = Parent,
spawned_as = MFArgs});
_ ->
- throw({inconsistent_trace_data, ?MODULE, ?LINE,
- [Pid, MFArgs, TS, Parent, Stack]})
+ %% In 19.0 we get both a spawn and spawned event,
+ %% however we do not know the order so we just ignore
+ %% the second event that comes
+ ok
end.
diff --git a/lib/tools/test/eprof_SUITE.erl b/lib/tools/test/eprof_SUITE.erl
index ba3b71cc1f..e908413315 100644
--- a/lib/tools/test/eprof_SUITE.erl
+++ b/lib/tools/test/eprof_SUITE.erl
@@ -81,9 +81,6 @@ basic(Config) when is_list(Config) ->
%% error case
- error = eprof:profile([Pid], fun() -> eprof_test:go(10) end),
- Pid = whereis(eprof),
- error = eprof:profile([Pid], fun() -> eprof_test:go(10) end),
A = spawn(fun() -> receive _ -> ok end end),
profiling = eprof:profile([A]),
true = exit(A, kill_it),
diff --git a/lib/tools/test/fprof_SUITE.erl b/lib/tools/test/fprof_SUITE.erl
index 244bdaaf0e..e18d384b52 100644
--- a/lib/tools/test/fprof_SUITE.erl
+++ b/lib/tools/test/fprof_SUITE.erl
@@ -985,10 +985,15 @@ handle_trace({trace_ts,Pid,spawn,NewPid,{M,F,Args},TS},P) ->
MFA = {M,F,length(Args)},
?dbg("~p",[{{spawn,Pid,NewPid,MFA},get(Pid)}]),
T = ts_sub(TS,get({Pid,last_ts})),
- put({NewPid,last_ts},TS),
- put(NewPid,[suspend,MFA]),
- insert(NewPid,suspend),
- insert(NewPid,MFA),
+ case get(NewPid) of
+ undefined ->
+ put({NewPid,last_ts},TS),
+ put(NewPid,[suspend,MFA]),
+ insert(NewPid,suspend),
+ insert(NewPid,MFA);
+ _Else ->
+ ok
+ end,
case get(Pid) of
[SpawningMFA|_] = Stack ->
update_own(Pid,SpawningMFA,T),
@@ -996,6 +1001,19 @@ handle_trace({trace_ts,Pid,spawn,NewPid,{M,F,Args},TS},P) ->
end,
put({Pid,last_ts},TS),
P;
+handle_trace({trace_ts,NewPid,spawned,Pid,{M,F,Args},TS},P) ->
+ MFA = {M,F,length(Args)},
+ ?dbg("~p",[{{spawned,NewPid,Pid,MFA},get(NewPid)}]),
+ case get(NewPid) of
+ undefined ->
+ put({NewPid,last_ts},TS),
+ put(NewPid,[suspend,MFA]),
+ insert(NewPid,suspend),
+ insert(NewPid,MFA);
+ _Else ->
+ ok
+ end,
+ P;
handle_trace({trace_ts,Pid,exit,_Reason,TS},P) ->
?dbg("~p",[{{exit,Pid,_Reason},get(Pid)}]),
T = ts_sub(TS,get({Pid,last_ts})),
@@ -1023,6 +1041,7 @@ handle_trace(end_of_trace,P) ->
P ! {result,[{totals,TotAcc,TotOwn}|ProcOwns]++Result},
P;
handle_trace(Other,_P) ->
+ ct:log("Got unexpected trace message: ~p",[Other]),
exit({unexpected,Other}).
find_return_to(MFA,[MFA|_]=Stack) ->