From 37092dab15448ef6a078800e3ff0cc41880ea765 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 10 Dec 2015 11:10:46 +0100 Subject: erts: Implement tracer modules Add the possibility to use modules as trace data receivers. The functions in the module have to be nifs as otherwise complex trace probes will be very hard to handle (complex means trace probes for ports for example). This commit changes the way that the ptab->tracer field works from always being an immediate, to now be NIL if no tracer is present or else be the tuple {TracerModule, TracerState} where TracerModule is an atom that is later used to lookup the appropriate tracer callbacks to call and TracerState is just passed to the tracer callback. The default process and port tracers have been rewritten to use the new API. This commit also changes the order which trace messages are delivered to the potential tracer process. Any enif_send done in a tracer module may be delayed indefinitely because of lock order issues. If a message is delayed any other trace message send from that process is also delayed so that order is preserved for each traced entity. This means that for some trace events (i.e. send/receive) the events may come in an unintuitive order (receive before send) to the trace receiver. Timestamps are taken when the trace message is generated so trace messages from differented processes may arrive with the timestamp out of order. Both the erlang:trace and seq_trace:set_system_tracer accept the new tracer module tracers and also the backwards compatible arguments. OTP-10267 --- lib/kernel/doc/src/seq_trace.xml | 11 ++--- lib/kernel/src/seq_trace.erl | 16 ++++++-- lib/kernel/test/seq_trace_SUITE.erl | 3 +- lib/runtime_tools/c_src/Makefile.in | 69 ++++++-------------------------- lib/runtime_tools/c_src/trace_file_drv.c | 13 +++++- lib/sasl/src/systools_make.erl | 2 +- 6 files changed, 45 insertions(+), 69 deletions(-) (limited to 'lib') 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

Sets the system tracer. The system tracer can be either a - process or port denoted by Tracer. Returns the previous - value (which can be false if no system tracer is - active).

+ process, port or tracer module + denoted by Tracer. + Returns the previous value (which can be false if no system + tracer is active).

Failure: {badarg, Info}} if Pid is not an existing local pid.

@@ -225,7 +226,7 @@ seq_trace:set_token(OldToken), % activate the trace token again Return the pid() or port() of the current system tracer. -

Returns the pid or port identifier of the current system +

Returns the pid, port identifier or tracer module of the current system tracer or false if no system tracer is activated.

@@ -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.

On each Erlang node, a process can be set as the system tracer. - 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 send or 'receive' 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/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/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]. -- cgit v1.2.3 From 6cb6b59cd4cd5bd4383053e12ae8ab192711c827 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 8 Feb 2016 18:31:43 +0100 Subject: erts: Extend process and port tracing This commit completes the tracing for processes so that all messages sent by a process (via nifs or otherwise) will be traced. The commit also adds tracing of all types of events from ports. When enabling tracing using erlang:trace, the 'all' flag now also enables tracing on all ports. OTP-13496 --- lib/runtime_tools/src/dbg.erl | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib') diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl index 6eea1a0917..7a4034d0b0 100644 --- a/lib/runtime_tools/src/dbg.erl +++ b/lib/runtime_tools/src/dbg.erl @@ -1188,6 +1188,11 @@ to_pidspec(X) when is_pid(X) -> true -> X; false -> {badpid,X} end; +to_pidspec(X) when is_port(X) -> + case erlang:port_info(X) of + undefined -> {badport, X}; + _ -> X + end; to_pidspec(new) -> new; to_pidspec(all) -> all; to_pidspec(existing) -> existing; @@ -1203,6 +1208,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),".", -- cgit v1.2.3 From 48ab07fe347d547b6985cf3e2fc7126277d5d7dd Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 23 Feb 2016 17:09:25 +0100 Subject: runtime_tools: Update dbg to work with tracing on ports OTP-13500 --- lib/runtime_tools/src/dbg.erl | 51 +++++++++++++++++++++++++----------- lib/runtime_tools/test/dbg_SUITE.erl | 33 +++++++++++++++++++++-- 2 files changed, 67 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl index 7a4034d0b0..9ab66c7f5c 100644 --- a/lib/runtime_tools/src/dbg.erl +++ b/lib/runtime_tools/src/dbg.erl @@ -1114,7 +1114,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 +1123,9 @@ 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,arity,return_to,silent,running_procs,running_ports]. display_info([Node|Nodes]) -> io:format("~nNode ~w:~n",[Node]), @@ -1146,24 +1146,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)]. @@ -1193,9 +1203,17 @@ to_pidspec(X) when is_port(X) -> undefined -> {badport, X}; _ -> X end; -to_pidspec(new) -> new; -to_pidspec(all) -> all; -to_pidspec(existing) -> existing; +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}; @@ -1223,9 +1241,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])). diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl index d40efcafef..458c6a2dea 100644 --- a/lib/runtime_tools/test/dbg_SUITE.erl +++ b/lib/runtime_tools/test/dbg_SUITE.erl @@ -22,7 +22,7 @@ %% 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, @@ -45,7 +45,7 @@ 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]. @@ -272,6 +272,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) -> -- cgit v1.2.3 From 17b74ad1f0b7095fd85ee8ffd96e8ff3b59a7e86 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 24 Feb 2016 15:02:10 +0100 Subject: observer: Update ttb to work with tracing on ports OTP-13500 --- lib/observer/src/ttb.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') 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 -- cgit v1.2.3 From a67fd61a89ca7ab583612358918985d46746101e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 26 Feb 2016 09:18:39 +0100 Subject: fprof: update to work with new spawned trace event OTP-13499 --- lib/tools/src/fprof.erl | 15 ++++++++++++--- lib/tools/test/fprof_SUITE.erl | 27 +++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) (limited to 'lib') 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/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) -> -- cgit v1.2.3 From ca74d2a97ee32a7127a30f7f347c3a8814acd444 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 16 Mar 2016 10:20:46 +0100 Subject: eprof: Fix tests after tracer module incompatabilities --- lib/tools/test/eprof_SUITE.erl | 3 --- 1 file changed, 3 deletions(-) (limited to 'lib') 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), -- cgit v1.2.3 From 2a0e30cb155b80273eb43bc0e01376b68a5fb5b0 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 17 Mar 2016 16:02:16 +0100 Subject: runtime_tools: Lots of dbg docs updates --- lib/runtime_tools/doc/src/dbg.xml | 281 ++++++++++++++++++++++---------------- 1 file changed, 165 insertions(+), 116 deletions(-) (limited to 'lib') diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml index 0232cc0453..ccb3bca50e 100644 --- a/lib/runtime_tools/doc/src/dbg.xml +++ b/lib/runtime_tools/doc/src/dbg.xml @@ -36,14 +36,30 @@ The Text Based Trace Facility

This module implements a text based interface to the - trace/3 and the trace_pattern/2 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 - pman module. -

-

For some examples of how to use dbg from the Erlang + trace/3 and the + trace_pattern/2 BIFs. It makes it + possible to trace functions, processes, ports and messages. +

+

+ To quickly get started on tracing function calls you can use the following + code in the Erlang shell: +

+
+1> dbg:tracer(). %% Start the default trace message receiver
+{ok,<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).
+(<0.34.0>) call lists:seq(1,10)
+(<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]
+    
+

+ For more examples of how to use dbg from the Erlang shell, see the simple example section. -

+

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)\

Traces Item in accordance to the value specified by Flags. The variation of Item is listed below:

- - If the Item is a pid(), 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 n/1 and - tracer/0/2/3). - If the Item is the atom all, all processes in the - system as well as all processes created hereafter are - to be traced. This also affects all nodes added with the - n/1 or tracer/0/2/3 function. - If the Item is the atom new, no currently existing - processes are affected, but every process created after the - call is.This also affects all nodes added with the - n/1 or tracer/0/2/3 function. - If the Item is the atom existing, all - existing processes are traced, but new processes will not - be affected.This also affects all nodes added with the - n/1 or tracer/0/2/3 function. - If the Item is an atom other than all, - new or existing, 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 n/1 or tracer/0/2/3 function. - If the Item is an integer, the process ]]> is - traced. - If the Item is a tuple {X, Y, Z}, the - process ]]> is - traced. + + pid() or port() + 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 n/1 + and tracer/3). + all + All processes and ports in the system as well as all processes and ports + created hereafter are to be traced. + all_processes + All processes in the system as well as all processes created hereafter are to be traced. + all_ports + All ports in the system as well as all ports created hereafter are to be traced. + new + All processes and ports created after the call is are to be traced. + new_processes + All processes created after the call is are to be traced. + new_ports + All ports created after the call is are to be traced. + existing + All existing processes and ports are traced. + existing_processes + All existing processes are traced. + existing_ports + All existing ports are traced. + atom() + 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 n/1 or + tracer/3 function. + integer() + The process ]]> is traced. + {X, Y, Z} + The process ]]> is traced. + string() If the Item is a string "]]> - as returned from pid_to_list/1, the process + as returned from pid_to_list/1, the process ]]> is traced. - + + +

When enabling an Item that represents a group of processes, + the Item is enabled on all nodes added with the + n/1 or + tracer/3 function.

+

Flags can be a single atom, or a list of flags. The available flags are:

s (send) -

Traces the messages the process sends.

+

Traces the messages the process or port sends.

r (receive) -

Traces the messages the process receives.

+

Traces the messages the process or port receives.

m (messages) -

Traces the messages the process receives and sends.

+

Traces the messages the process or port receives and sends.

c (call) @@ -221,6 +250,10 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

Traces process related events to the process.

+ ports + +

Traces port related events to the port.

+
sos (set on spawn)

Lets all processes created by the traced @@ -241,8 +274,8 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ sofl (set on first link)

This is the same as sol, but only for - the first call to - link/1 by the traced process.

+ the first call to + link/1 by the traced process.

all @@ -258,7 +291,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ erlang:trace/3

The function returns either an error tuple or a tuple {ok, List}. The List 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 {matched, Node, N}. If the remote processor call,rpc, to a remote node fails, @@ -286,9 +319,9 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ i() -> ok - Display information about all traced processes. + Display information about all traced processes and ports. -

Displays information about all traced processes.

+

Displays information about all traced processes and ports.

@@ -327,35 +360,41 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

This function enables call trace for one or more - functions. All exported functions matching the {Module, Function, Arity} argument will be concerned, but the + functions. All exported functions matching the {Module, Function, Arity} + argument will be concerned, but the match_spec() may further narrow down the set of function calls generating trace messages.

For a description of the match_spec() syntax, please turn to the User's guide part of the online documentation for the runtime system (erts). The - chapter Match Specification in Erlang explains the - general match specification "language".

+ chapter Match Specifications in Erlang + explains the general match specification "language". + The most common generic match specifications used can be + found as Built-inAlias', see + ltp/0 below for details. +

The Module, Function and/or Arity parts of the tuple may be specified as the atom '_' which is a "wild-card" matching all modules/functions/arities. Note, if the Module is specified as '_', the Function and Arity parts have to be specified as '_' too. The same holds for the Functions relation to the Arity.

-

All nodes added with n/1 or tracer/0/2/3 will +

All nodes added with n/1 or + tracer/3 will be affected by this call, and if Module is not '_' the module will be loaded on all nodes.

The function returns either an error tuple or a tuple {ok, List}. The List consists of specifications of how - many functions that matched, in the same way as the processes - are presented in the return value of p/2.

+ many functions that matched, in the same way as the processes and ports + are presented in the return value of p/2.

There may be a tuple {saved, N} in the return value, if the MatchSpec is other than []. The integer N 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 ltp/0 below - for details.

+ built-in aliases for common expressions, see + ltp/0 below for details.

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 {error, string()} where @@ -394,7 +433,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ tpl({Module, Function, Arity}, MatchSpec) -> {ok, MatchDesc} | {error, term()} Set pattern for traced local (as well as global) function calls -

This function works as tp/2, but enables +

This function works as tp/2, but enables tracing for local calls (and local functions) as well as for global calls (and functions).

@@ -441,10 +480,10 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

This function disables call tracing on the specified functions. The semantics of the parameter is the same as for the corresponding function specification in - tp/2 or tpl/2. Both local and global call trace + tp/2 or tpl/2. Both local and global call trace is disabled.

The return value reflects how many functions that matched, - and is constructed as described in tp/2. No tuple + and is constructed as described in tp/2. No tuple {saved, N} is however ever returned (for obvious reasons).

@@ -480,8 +519,8 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ ctpl({Module, Function, Arity}) -> {ok, MatchDesc} | {error, term()} Clear call trace pattern for the specified functions -

This function works as ctp/1, but only disables - tracing set up with tpl/2 (not with tp/2).

+

This function works as ctp/1, but only disables + tracing set up with tpl/2 (not with tp/2).

@@ -516,8 +555,8 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ ctpg({Module, Function, Arity}) -> {ok, MatchDesc} | {error, term()} Clear call trace pattern for the specified functions -

This function works as ctp/1, but only disables - tracing set up with tp/2 (not with tpl/2).

+

This function works as ctp/1, but only disables + tracing set up with tp/2 (not with tpl/2).

@@ -526,13 +565,13 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

Use this function to recall all match specifications previously used in the session (i. e. previously saved during calls - to tp/2, and built-in match specifications. + to tp/2, 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 stop/0 is called.

+ match specifications are lost if stop/0 is called.

Match specifications used can be saved in a file (if a read-write file system is present) for use in later - debugging sessions, see wtp/1 and rtp/1

+ debugging sessions, see wtp/1 and rtp/1

There are three built-in trace patterns: exception_trace, caller_trace and caller_exception_trace (or x, c and @@ -555,10 +594,10 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ Delete all saved match specifications.

Use this function to "forget" all match specifications - saved during calls to tp/2. + saved during calls to tp/2. This is useful when one wants to restore other match - specifications from a file with rtp/1. Use - dtp/1 to delete specific saved match specifications.

+ specifications from a file with rtp/1. Use + dtp/1 to delete specific saved match specifications.

@@ -569,7 +608,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

Use this function to "forget" a specific match specification - saved during calls to tp/2.

+ saved during calls to tp/2.

@@ -581,12 +620,12 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

This function will save all match specifications saved - during the session (during calls to tp/2) + during the session (during calls to tp/2) and built-in match specifications in a text file with the name designated by Name. The format of the file is textual, why it can be edited with an ordinary text editor, and then restored with - rtp/1.

+ rtp/1.

Each match spec in the file ends with a full stop (.) and new (syntactically correct) match specifications can be added to the file manually.

@@ -604,7 +643,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

This function reads match specifications from a file - (possibly) generated by the wtp/1 function. It checks + (possibly) generated by the wtp/1 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.

The match specifications in the file are merged with the current match specifications, so that no duplicates - are generated. Use ltp/0 to see what numbers were + are generated. Use ltp/0 to see what numbers were assigned to the specifications from the file.

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. -

+ to what's causing the problem. +

@@ -631,12 +670,12 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

The dbg server keeps a list of nodes where tracing - should be performed. Whenever a tp/2 call or a - p/2 call is made, it is executed for all nodes in this - list including the local node (except for p/2 with a - specific pid() as first argument, in which case the + should be performed. Whenever a tp/2 call or a + p/2 call is made, it is executed for all nodes in this + list including the local node (except for p/2 with a + specific pid() or port() as first argument, in which case the command is executed only on the node where the designated - process resides). + process or port resides).

This function adds a remote node (Nodename) 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 no_local_tracer is returned. The tracer process on the local node must be started with the - tracer/0/2 function. + tracer/0/2 function.

If Nodename is the local node, the error reason cant_add_local_node is returned.

-

If a trace port (see trace_port/2) is +

If a trace port (see trace_port/2) is running on the local node, remote nodes can not be traced with a tracer process. The error reason cant_trace_remote_pid_to_local_port is returned. A trace port can however be started on the remote node with the - tracer/3 function. + tracer/3 function.

The function will also return an error if the node Nodename is not reachable.

@@ -669,7 +708,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

Clears a node from the list of traced nodes. Subsequent - calls to tp/2 and p/2 will not consider that + calls to tp/2 and p/2 will not consider that node, but tracing already activated on the node will continue to be in effect.

Returns ok, cannot fail.

@@ -688,37 +727,42 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

This function starts a server on the local node that will be the recipient of all trace messages. All subsequent calls - to p/2 will result in messages sent to the newly + to p/2 will result in messages sent to the newly started trace server.

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 tracer/2 for a description - of how the trace message handler can be customized. -

-

To start a similar tracer on a remote node, use n/1.

+ (i. e. use io:format). See tracer/2 for a description + of how the trace message handler can be customized. +

+

To start a similar tracer on a remote node, use n/1.

tracer(Type, Data) -> {ok, pid()} | {error, Error} Start a tracer server with additional parameters - Type = port | process - Data = PortGenerator | HandlerSpec - HandlerSpec = {HandlerFun, InitialData} - HandlerFun = fun() (two arguments) - InitialData = term() + Type = port | process | module + Data = PortGenerator | HandlerSpec | ModuleSpec PortGenerator = fun() (no arguments) Error = term() + HandlerSpec = {HandlerFun, InitialData} + HandlerFun = fun() (two arguments) + ModuleSpec = {TracerModule, TracerState} + ModuleModule = atom() + InitialData = TracerState = term()

This function starts a tracer server with additional parameters on the local node. The first parameter, the Type, indicates if trace messages should be handled - by a receiving process (process) or by a tracer port - (port). For a description about tracer ports see - trace_port/2. + by a receiving process (process), by a tracer port + (port) or by a tracer module + (module). For a description about tracer ports see + trace_port/2 + and for a tracer modules see + erl_tracer.

-

If Type is a process, a message handler function can +

If Type is process, a message handler function can be specified (HandlerSpec). The handler function, which should be a fun 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.

-

If Type is a port, then the second parameter should +

If Type is port, then the second parameter should be a fun which takes no arguments and returns a newly opened trace port when called. Such a fun is - preferably generated by calling trace_port/2. + preferably generated by calling trace_port/2.

+

if Type is module, then the second parameter should + be a tuple describing the erl_tracer + module to be used for tracing and the state to be used for + that tracer module.

If an error is returned, it can either be due to a tracer server already running ({error,already_started}) or due to the HandlerFun throwing an exception.

To start a similar tracer on a remote node, use - tracer/3. -

+ tracer/3. +

@@ -750,20 +798,20 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ Nodename = atom() -

This function is equivalent to tracer/2, but acts on +

This function is equivalent to tracer/2, but acts on the given node. A tracer is started on the node (Nodename) and the node is added to the list of traced nodes.

-

This function is not equivalent to n/1. While - n/1 starts a process tracer which redirects all trace +

This function is not equivalent to n/1. While + n/1 starts a process tracer which redirects all trace information to a process tracer on the local node (i.e. the - trace control node), tracer/3 starts a tracer of any + trace control node), tracer/3 starts a tracer of any type which is independent of the tracer on the trace control node.

-

For details, see tracer/2.

+

For details, see tracer/2.

@@ -795,9 +843,9 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ file and the ip 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 - trace_client/2 function. The ip driver opens a TCP/IP + trace_client/2 function. The ip driver opens a TCP/IP port where it listens for connections. When a client - (preferably started by calling trace_client/2 on + (preferably started by calling trace_client/2 on another Erlang node) connects, all trace messages are sent over the IP network for further processing by the remote client.

@@ -836,7 +884,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 trace_client/3 as the tuple {drop, N} where N is the number of consecutive messages + specified in trace_client/3 as the tuple {drop, N} where N 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.

@@ -890,7 +938,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ get_listen_port -

Returns {ok, IpPort} where IpPortis +

Returns {ok, IpPort} where IpPort is the IP port number used by the driver listen socket. Only the ip trace driver supports this operation.

@@ -913,7 +961,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

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 tracer/0 + same way as a tracer process created by the tracer/0 function.

If Type is file, the client reads all trace messages stored in the file named Filename or @@ -925,7 +973,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

If Type is follow_file, the client behaves as in the file case, but keeps trying to read (and process) more data - from the file until stopped by stop_trace_client/1. + from the file until stopped by stop_trace_client/1. WrapFilesSpec is not allowed as second argument for this Type.

If Type is ip, the client connects to the @@ -981,10 +1029,10 @@ hello InitialData = term() -

This function works exactly as trace_client/2, but +

This function works exactly as trace_client/2, but allows you to write your own handler function. The handler function works mostly as the one described in - tracer/2, but will also have to be prepared to handle + tracer/2, but will also have to be prepared to handle trace messages of the form {drop, N}, where N is the number of dropped messages. This pseudo trace message will only occur if the ip trace driver is used.

@@ -1003,7 +1051,7 @@ hello

This function shuts down a previously started trace client. The Pid argument is the process id returned - from the trace_client/2 or trace_client/3 call.

+ from the trace_client/2 or trace_client/3 call.

@@ -1156,8 +1204,9 @@ SeqTrace [0]: (<0.30.0>) <0.25.0> ! {dbg,{ok,<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 io function such as format/2. Note that when - dbg:p(all,call) is called, IO processes are also traced. + prints to tty using an io function such as format/2. + Note that when + dbg:p(all,call) is called, IO processes are also traced. Here's an example:

 %% Using a default line editing shell
-- 
cgit v1.2.3


From ec6615584daa1ec4429127ca8ff6e506c72cddb9 Mon Sep 17 00:00:00 2001
From: Lukas Larsson 
Date: Fri, 18 Mar 2016 17:36:46 +0100
Subject: runtime_rools: Allow new timestamp trace flags

OTP-13502
---
 lib/runtime_tools/src/dbg.erl | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'lib')

diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index 9ab66c7f5c..3a6eaae16d 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -1125,7 +1125,8 @@ transform_flags(Bad,_Acc) -> {error,{bad_flags,Bad}}.
 all() ->
     [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,running_procs,running_ports].
+     timestamp,monotonic_timestamp,strict_monotonic_timestamp,
+     arity,return_to,silent,running_procs,running_ports].
 
 display_info([Node|Nodes]) ->
     io:format("~nNode ~w:~n",[Node]),
-- 
cgit v1.2.3


From 6117ee72af5c0c2973a0f20fb847a216825db03f Mon Sep 17 00:00:00 2001
From: Lukas Larsson 
Date: Fri, 18 Mar 2016 19:46:04 +0100
Subject: runtime_tools: Make dbg work with erl_tracer modules

OTP-13500
---
 lib/runtime_tools/doc/src/dbg.xml                  |  17 ++--
 lib/runtime_tools/src/dbg.erl                      |  35 +++++--
 lib/runtime_tools/test/dbg_SUITE.erl               |  79 +++++++++++++-
 lib/runtime_tools/test/dbg_SUITE_data/Makefile.src |   8 ++
 lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c  | 113 +++++++++++++++++++++
 5 files changed, 235 insertions(+), 17 deletions(-)
 create mode 100644 lib/runtime_tools/test/dbg_SUITE_data/Makefile.src
 create mode 100644 lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c

(limited to 'lib')

diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml
index ccb3bca50e..0128e23a47 100644
--- a/lib/runtime_tools/doc/src/dbg.xml
+++ b/lib/runtime_tools/doc/src/dbg.xml
@@ -288,7 +288,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
           
         
         

The list can also include any of the flags allowed in - erlang:trace/3

+ erlang:trace/3

The function returns either an error tuple or a tuple {ok, List}. The List consists of specifications of how many processes and ports that matched (in the @@ -747,7 +747,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ Error = term() HandlerSpec = {HandlerFun, InitialData} HandlerFun = fun() (two arguments) - ModuleSpec = {TracerModule, TracerState} + ModuleSpec = fun() (no arguments) | {TracerModule, TracerState} ModuleModule = atom() InitialData = TracerState = term() @@ -779,9 +779,9 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ preferably generated by calling trace_port/2.

if Type is module, then the second parameter should - be a tuple describing the erl_tracer + be either a tuple describing the erl_tracer module to be used for tracing and the state to be used for - that tracer module.

+ that tracer module or a fun returning the same tuple.

If an error is returned, it can either be due to a tracer server already running ({error,already_started}) or due to the HandlerFun throwing an exception. @@ -800,8 +800,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\

This function is equivalent to tracer/2, but acts on the given node. A tracer is started on the node - (Nodename) and the node is added to the list of traced - nodes. + (Nodename) and the node is added to the list of traced nodes.

This function is not equivalent to n/1. While @@ -1066,11 +1065,11 @@ hello

Return the process or port to which all trace messages are sent. Nodename = atom() - Tracer = port() | pid() + Tracer = port() | pid() | {module(), term()} -

Returns the process or port to which all trace - messages are sent.

+

Returns the process, port or tracer module to which all trace + messages are sent.

diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl index 3a6eaae16d..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} -> @@ -1439,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 458c6a2dea..17c04c0ed4 100644 --- a/lib/runtime_tools/test/dbg_SUITE.erl +++ b/lib/runtime_tools/test/dbg_SUITE.erl @@ -26,10 +26,12 @@ 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)). @@ -48,7 +50,8 @@ all() -> [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() -> []. @@ -805,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 +#include +#include +#include + +/* 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; +} -- cgit v1.2.3 From 8e2ec999394c77741241ef1a12728b11195961c8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 21 Mar 2016 17:53:26 +0100 Subject: erts: Document erlang:match_spec_test/3 OTP-13501 --- lib/stdlib/doc/src/ets.xml | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') 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 if the match_spec does not match the object Tuple.

This is a useful debugging and test tool, especially when writing complicated ets:select/2 calls.

+

See also: + erlang:match_spec_test/3.

-- cgit v1.2.3