diff options
Diffstat (limited to 'lib/runtime_tools')
47 files changed, 3317 insertions, 169 deletions
diff --git a/lib/runtime_tools/c_src/Makefile.in b/lib/runtime_tools/c_src/Makefile.in index 840de39f07..cbbcfa2d56 100644 --- a/lib/runtime_tools/c_src/Makefile.in +++ b/lib/runtime_tools/c_src/Makefile.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2010. All Rights Reserved. +# Copyright Ericsson AB 1999-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -21,6 +21,11 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk include $(ERL_TOP)/make/$(TARGET)/otp_ded.mk # ---------------------------------------------------- +# Items from top-level configure +# ---------------------------------------------------- +DTRACE_ENABLED=@DTRACE_ENABLED@ +DTRACE_ENABLED_2STEP=@DTRACE_ENABLED_2STEP@ +# ---------------------------------------------------- # Application version # ---------------------------------------------------- include ../vsn.mk @@ -38,6 +43,8 @@ SHELL = /bin/sh LIBS = $(DED_LIBS) LDFLAGS += $(DED_LDFLAGS) +DTRACE_LIBNAME = dyntrace + SYSINCLUDE = $(DED_SYS_INCLUDE) ifeq ($(findstring vxworks,$(TARGET)),vxworks) SYSINCLUDE += -I$(ERL_TOP)/erts/etc/vxworks @@ -45,15 +52,20 @@ endif TRACE_DRV_INCLUDES = $(SYSINCLUDE) -ALL_CFLAGS = $(CFLAGS) @DEFS@ $(TYPE_FLAGS) $(TRACE_DRV_INCLUDES) - +ALL_CFLAGS = $(CFLAGS) @DEFS@ $(TYPE_FLAGS) $(TRACE_DRV_INCLUDES) \ + -I$(OBJDIR) -I$(ERL_TOP)/erts/emulator/$(TARGET) ifeq ($(TYPE),debug) TYPEMARKER = .debug -TYPE_FLAGS = -g -DDEBUG @DEBUG_FLAGS@ +TYPE_FLAGS = $(subst -O3,,$(subst -O2,,$(CFLAGS))) -DDEBUG @DEBUG_FLAGS@ +else +ifeq ($(TYPE),valgrind) +TYPEMARKER = .valgrind +TYPE_FLAGS = $(subst -O3,,$(subst -O2,,$(CFLAGS))) -DVALGRIND else TYPEMARKER = -TYPE_FLAGS = -O2 +TYPE_FLAGS = $(CFLAGS) +endif endif ROOTDIR = $(ERL_TOP)/lib @@ -69,6 +81,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@ + +ifeq ($(HOST_OS),) +HOST_OS := $(shell $(ERL_TOP)/erts/autoconf/config.guess) +endif + TRACE_IP_DRV_OBJS = \ $(OBJDIR)/trace_ip_drv.o @@ -89,46 +111,78 @@ endif # Targets # ---------------------------------------------------- -debug opt: $(OBJDIR) $(BINDIR) $(SOLIBS) +_create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR)) + +debug opt valgrind: $(SOLIBS) $(OBJDIR) $(LIBDIR) $(NIF_LIB) + +ifdef DTRACE_ENABLED +DTRACE_USER_HEADER=$(OBJDIR)/dtrace_user.h +$(OBJDIR)/dtrace_user.h: ./dtrace_user.d + dtrace -h -C $(INCLUDES) \ + -s ./dtrace_user.d \ + -o ./dtrace_user.tmp + sed -e '/^#define[ ]*ERLANG_[A-Z0-9_]*(.*)/y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' ./dtrace_user.tmp > $@ + rm ./dtrace_user.tmp +else +DTRACE_USER_HEADER= +endif + +DTRACE_OBJS = +ifdef DTRACE_ENABLED_2STEP +DTRACE_OBJS += $(OBJDIR)/dtrace_user.o +$(OBJDIR)/dtrace_user.o: $(before_DTrace_OBJS) $(OBJDIR)/dtrace_user.h + dtrace -G -C \ + -s ./dtrace_user.d \ + -o $@ $(before_DTrace_OBJS) +endif + +DYNTRACE_OBJS = $(before_DTrace_OBJS) $(DTRACE_OBJS) $(OBJDIR): -@mkdir -p $(OBJDIR) -$(BINDIR): - -@mkdir -p $(BINDIR) +$(LIBDIR): + -@mkdir -p $(LIBDIR) -$(OBJDIR)/%.o: %.c +$(OBJDIR)/dyntrace$(TYPEMARKER).o: dyntrace.c $(DTRACE_USER_HEADER) $(INSTALL_DIR) $(OBJDIR) $(CC) -c -o $@ $(ALL_CFLAGS) $< -$(LIBDIR)/trace_ip_drv.so: $(TRACE_IP_DRV_OBJS) +$(NIF_LIB): $(DYNTRACE_OBJS) $(INSTALL_DIR) $(LIBDIR) + $(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +$(OBJDIR)/%.o: %.c + $(CC) -c -o $@ $(ALL_CFLAGS) $< + +$(LIBDIR)/trace_ip_drv.so: $(TRACE_IP_DRV_OBJS) $(LD) $(LDFLAGS) -o $@ $^ -lc $(LIBS) $(LIBDIR)/trace_file_drv.so: $(TRACE_FILE_DRV_OBJS) - $(INSTALL_DIR) $(LIBDIR) $(LD) $(LDFLAGS) -o $@ $^ -lc $(LIBS) $(LIBDIR)/trace_ip_drv.dll: $(TRACE_IP_DRV_OBJS) - $(INSTALL_DIR) $(LIBDIR) $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) $(LIBDIR)/trace_file_drv.dll: $(TRACE_FILE_DRV_OBJS) - $(INSTALL_DIR) $(LIBDIR) $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) # # VxWorks is simply to different from Unix in this sense. # Here are the inference rules for VxWorks # $(LIBDIR)/trace_ip_drv.eld: $(TRACE_IP_DRV_OBJS) - $(INSTALL_DIR) $(LIBDIR) $(LD) $(LDFLAGS) -o $@ $^ $(LIBDIR)/trace_file_drv.eld: $(TRACE_FILE_DRV_OBJS) - $(INSTALL_DIR) $(LIBDIR) $(LD) $(LDFLAGS) -o $@ $^ 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 core *~ docs: @@ -139,8 +193,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/priv/obj $(INSTALL_DIR) $(RELSYSDIR)/priv/lib - $(INSTALL_PROGRAM) $(SOLIBS) $(RELSYSDIR)/priv/lib + $(INSTALL_PROGRAM) $(DYNTRACE_OBJS) $(RELSYSDIR)/priv/obj + $(INSTALL_PROGRAM) $(NIF_LIB) $(SOLIBS) $(RELSYSDIR)/priv/lib release_docs_spec: diff --git a/lib/runtime_tools/c_src/dtrace_user.d b/lib/runtime_tools/c_src/dtrace_user.d new file mode 100644 index 0000000000..3a80d0f7a3 --- /dev/null +++ b/lib/runtime_tools/c_src/dtrace_user.d @@ -0,0 +1,53 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. + * All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +provider erlang { + /** + * Send a single string to a probe. + * + * @param NUL-terminated string + */ + probe user_trace__s1(char* message); + + /** + * Multi-purpose probe: up to 4 NUL-terminated strings and 4 + * 64-bit integer arguments. + * + * @param proc, the PID (string form) of the sending process + * @param user_tag, the user tag of the sender + * @param i1, integer + * @param i2, integer + * @param i3, integer + * @param i4, integer + * @param s1, string/iolist. D's arg6 is NULL if not given by Erlang + * @param s2, string/iolist. D's arg7 is NULL if not given by Erlang + * @param s3, string/iolist. D's arg8 is NULL if not given by Erlang + * @param s4, string/iolist. D's arg9 is NULL if not given by Erlang + */ + probe user_trace__i4s4(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); +}; + +#pragma D attributes Evolving/Evolving/Common provider erlang provider +#pragma D attributes Private/Private/Common provider erlang module +#pragma D attributes Private/Private/Common provider erlang function +#pragma D attributes Evolving/Evolving/Common provider erlang name +#pragma D attributes Evolving/Evolving/Common provider erlang args diff --git a/lib/runtime_tools/c_src/dyntrace.c b/lib/runtime_tools/c_src/dyntrace.c new file mode 100644 index 0000000000..96dbebbdfa --- /dev/null +++ b/lib/runtime_tools/c_src/dyntrace.c @@ -0,0 +1,172 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* + * Purpose: Dynamically loadable NIF library for DTrace + */ + + + +#include "erl_nif.h" +#include "config.h" +#include "sys.h" +#include "dtrace-wrapper.h" +#if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP)) +#define HAVE_USE_DTRACE 1 +#endif +#ifdef HAVE_USE_DTRACE +#include "dtrace_user.h" +#endif + +void dtrace_nifenv_str(ErlNifEnv *env, char *process_buf); +void get_string_maybe(ErlNifEnv *env, const ERL_NIF_TERM term, char **ptr, char *buf, int bufsiz); + +#ifdef VALGRIND + # include <valgrind/memcheck.h> +#endif + +#ifdef __GNUC__ + # define INLINE __inline__ +#else + # define INLINE +#endif + +#define MESSAGE_BUFSIZ 1024 + +/* NIF interface declarations */ +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); + +/* The NIFs: */ +static ERL_NIF_TERM available(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +static ErlNifFunc nif_funcs[] = { + {"available", 0, available}, + {"user_trace_s1", 1, user_trace_s1}, + {"user_trace_i4s4", 9, user_trace_i4s4} +}; + +ERL_NIF_INIT(dyntrace, nif_funcs, load, NULL, NULL, NULL) + +static ERL_NIF_TERM atom_true; +static ERL_NIF_TERM atom_false; +static ERL_NIF_TERM atom_error; +static ERL_NIF_TERM atom_not_available; +static ERL_NIF_TERM atom_badarg; +static ERL_NIF_TERM atom_ok; + +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + atom_true = enif_make_atom(env,"true"); + atom_false = enif_make_atom(env,"false"); + atom_error = enif_make_atom(env,"error"); + atom_not_available = enif_make_atom(env,"not_available"); + atom_badarg = enif_make_atom(env,"badarg"); + atom_ok = enif_make_atom(env,"ok"); + + return 0; +} + +static ERL_NIF_TERM available(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef HAVE_USE_DTRACE + return atom_true; +#else + return atom_false; +#endif +} + +static ERL_NIF_TERM user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef HAVE_USE_DTRACE + ErlNifBinary message_bin; + DTRACE_CHARBUF(messagebuf, MESSAGE_BUFSIZ + 1); + + if (DTRACE_ENABLED(user_trace_s1)) { + if (!enif_inspect_iolist_as_binary(env, argv[0], &message_bin) || + message_bin.size > MESSAGE_BUFSIZ) { + return atom_badarg; + } + memcpy(messagebuf, (char *) message_bin.data, message_bin.size); + messagebuf[message_bin.size] = '\0'; + DTRACE1(user_trace_s1, messagebuf); + return atom_true; + } else { + return atom_false; + } +#else + return atom_error; +#endif +} + +void +get_string_maybe(ErlNifEnv *env, + const ERL_NIF_TERM term, char **ptr, char *buf, int bufsiz) +{ + ErlNifBinary str_bin; + + if (!enif_inspect_iolist_as_binary(env, term, &str_bin) || + str_bin.size > bufsiz) { + *ptr = NULL; + } else { + memcpy(buf, (char *) str_bin.data, str_bin.size); + buf[str_bin.size] = '\0'; + *ptr = buf; + } +} + +static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef HAVE_USE_DTRACE + DTRACE_CHARBUF(procbuf, 32 + 1); + DTRACE_CHARBUF(user_tagbuf, MESSAGE_BUFSIZ + 1); + char *utbuf = NULL; + ErlNifSInt64 i1, i2, i3, i4; + DTRACE_CHARBUF(messagebuf1, MESSAGE_BUFSIZ + 1); + DTRACE_CHARBUF(messagebuf2, MESSAGE_BUFSIZ + 1); + DTRACE_CHARBUF(messagebuf3, MESSAGE_BUFSIZ + 1); + DTRACE_CHARBUF(messagebuf4, MESSAGE_BUFSIZ + 1); + char *mbuf1 = NULL, *mbuf2 = NULL, *mbuf3 = NULL, *mbuf4 = NULL; + + if (DTRACE_ENABLED(user_trace_i4s4)) { + dtrace_nifenv_str(env, procbuf); + get_string_maybe(env, argv[0], &utbuf, user_tagbuf, MESSAGE_BUFSIZ); + if (! enif_get_int64(env, argv[1], &i1)) + i1 = 0; + if (! enif_get_int64(env, argv[2], &i2)) + i2 = 0; + if (! enif_get_int64(env, argv[3], &i3)) + i3 = 0; + if (! enif_get_int64(env, argv[4], &i4)) + i4 = 0; + get_string_maybe(env, argv[5], &mbuf1, messagebuf1, MESSAGE_BUFSIZ); + get_string_maybe(env, argv[6], &mbuf2, messagebuf2, MESSAGE_BUFSIZ); + get_string_maybe(env, argv[7], &mbuf3, messagebuf3, MESSAGE_BUFSIZ); + get_string_maybe(env, argv[8], &mbuf4, messagebuf4, MESSAGE_BUFSIZ); + DTRACE10(user_trace_i4s4, procbuf, utbuf, + i1, i2, i3, i4, mbuf1, mbuf2, mbuf3, mbuf4); + return atom_true; + } else { + return atom_false; + } +#else + return atom_error; +#endif +} diff --git a/lib/runtime_tools/c_src/trace_file_drv.c b/lib/runtime_tools/c_src/trace_file_drv.c index 668f6f4af3..08bace80ef 100644 --- a/lib/runtime_tools/c_src/trace_file_drv.c +++ b/lib/runtime_tools/c_src/trace_file_drv.c @@ -21,6 +21,9 @@ * Purpose: Send trace messages to a file. */ +#ifdef __WIN32__ +#include <windows.h> +#endif #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -31,7 +34,6 @@ #ifdef __WIN32__ # include <io.h> # define write _write -# define open _open # define close _close # define unlink _unlink #else @@ -40,11 +42,6 @@ #include <errno.h> #include <sys/types.h> #include <fcntl.h> -#ifdef VXWORKS -# include "reclaim.h" -#endif - - /* * Deduce MAXPATHLEN, which is the one to use in this file, @@ -176,11 +173,13 @@ static TraceFileData *first_data; */ 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, int bufflen); +static void trace_file_output(ErlDrvData handle, char *buff, + ErlDrvSizeT bufflen); static void trace_file_finish(void); -static int trace_file_control(ErlDrvData handle, unsigned int command, - char* buff, int count, - char** res, int res_size); +static ErlDrvSSizeT trace_file_control(ErlDrvData handle, + unsigned int command, + char* buff, ErlDrvSizeT count, + char** res, ErlDrvSizeT res_size); static void trace_file_timeout(ErlDrvData handle); /* @@ -194,6 +193,12 @@ static int my_flush(TraceFileData *data); static void put_be(unsigned n, unsigned char *s); static void close_unlink_port(TraceFileData *data); static int wrap_file(TraceFileData *data); +#ifdef __WIN32__ +static int win_open(char *path, int flags, int mask); +#define open win_open +#else +ErlDrvEntry *driver_init(void); +#endif /* ** The driver struct @@ -212,7 +217,18 @@ 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 */ + NULL, /* F_PTR outputv, reserved */ + NULL, /* ready_async */ + NULL, /* flush */ + NULL, /* call */ + NULL, /* event */ + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, + NULL, + NULL, + NULL, }; /* @@ -241,6 +257,7 @@ static ErlDrvData trace_file_start(ErlDrvPort port, char *buff) int n, w; static const char name[] = "trace_file_drv"; + #ifdef HARDDEBUG fprintf(stderr,"hello (%s)\r\n", buff); #endif @@ -347,17 +364,18 @@ static void trace_file_stop(ErlDrvData handle) /* ** Data sent from erlang to port. */ -static void trace_file_output(ErlDrvData handle, char *buff, int bufflen) +static void trace_file_output(ErlDrvData handle, char *buff, + ErlDrvSizeT bufflen) { int heavy = 0; TraceFileData *data = (TraceFileData *) handle; unsigned char b[5] = ""; put_be((unsigned) bufflen, b + 1); - switch (my_write(data, b, sizeof(b))) { + switch (my_write(data, (unsigned char *) b, sizeof(b))) { case 1: heavy = !0; case 0: - switch (my_write(data, buff, bufflen)) { + switch (my_write(data, (unsigned char *) buff, bufflen)) { case 1: heavy = !0; case 0: @@ -391,9 +409,10 @@ static void trace_file_output(ErlDrvData handle, char *buff, int bufflen) /* ** Control message from erlang, we handle $f, which is flush. */ -static int trace_file_control(ErlDrvData handle, unsigned int command, - char* buff, int count, - char** res, int res_size) +static ErlDrvSSizeT trace_file_control(ErlDrvData handle, + unsigned int command, + char* buff, ErlDrvSizeT count, + char** res, ErlDrvSizeT res_size) { if (command == 'f') { TraceFileData *data = (TraceFileData *) handle; @@ -636,3 +655,40 @@ static int wrap_file(TraceFileData *data) { return 0; } +#ifdef __WIN32__ +static int win_open(char *path, int flags, int mask) +{ + DWORD access = 0; + DWORD creation = 0; + HANDLE fd; + int ret; + if (flags & O_WRONLY) { + access = GENERIC_WRITE; + } else if (flags & O_RDONLY) { + access = GENERIC_READ; + } else { + access = (GENERIC_READ | GENERIC_WRITE); + } + + if (flags & O_CREAT) { + creation |= CREATE_ALWAYS; + } else { + creation |= OPEN_ALWAYS; + } + + fd = CreateFileA(path, access, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + if (fd == INVALID_HANDLE_VALUE) { + + return -1; + } + + if ((ret = _open_osfhandle((intptr_t)fd, (flags & O_RDONLY) ? O_RDONLY : 0)) + < 0) { + CloseHandle(fd); + } + + return ret; +} +#endif diff --git a/lib/runtime_tools/c_src/trace_ip_drv.c b/lib/runtime_tools/c_src/trace_ip_drv.c index d2ed1a294b..7f7ab8dd9d 100644 --- a/lib/runtime_tools/c_src/trace_ip_drv.c +++ b/lib/runtime_tools/c_src/trace_ip_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2009. All Rights Reserved. + * Copyright Ericsson AB 1999-2012. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -185,7 +185,8 @@ static TraceIpData *first_data; */ static ErlDrvData trace_ip_start(ErlDrvPort port, char *buff); static void trace_ip_stop(ErlDrvData handle); -static void trace_ip_output(ErlDrvData handle, char *buff, int bufflen); +static void trace_ip_output(ErlDrvData handle, char *buff, + ErlDrvSizeT bufflen); #ifdef __WIN32__ static void trace_ip_event(ErlDrvData handle, ErlDrvEvent event); #endif @@ -193,9 +194,10 @@ static void trace_ip_ready_input(ErlDrvData handle, ErlDrvEvent fd); static void trace_ip_ready_output(ErlDrvData handle, ErlDrvEvent fd); static void trace_ip_finish(void); /* No arguments, despite what might be stated in any documentation */ -static int trace_ip_control(ErlDrvData handle, unsigned int command, - char* buff, int count, - char** res, int res_size); +static ErlDrvSSizeT trace_ip_control(ErlDrvData handle, + unsigned int command, + char* buff, ErlDrvSizeT count, + char** res, ErlDrvSizeT res_size); /* ** Internal routines @@ -210,11 +212,11 @@ static TraceIpData *lookup_data_by_port(int portno); static int set_nonblocking(SOCKET sock); static TraceIpMessage *make_buffer(int datasiz, unsigned char op, unsigned number); -static void enque_message(TraceIpData *data, unsigned char *buff, int bufflen, +static void enque_message(TraceIpData *data, char *buff, int bufflen, int byteswritten); static void clean_que(TraceIpData *data); static void close_client(TraceIpData *data); -static int trywrite(TraceIpData *data, unsigned char *buff, int bufflen); +static int trywrite(TraceIpData *data, char *buff, int bufflen); static SOCKET my_accept(SOCKET sock); static void close_unlink_port(TraceIpData *data); enum MySelectOp { SELECT_ON, SELECT_OFF, SELECT_CLOSE }; @@ -382,7 +384,7 @@ static void trace_ip_stop(ErlDrvData handle) /* ** Data sent from erlang to port. */ -static void trace_ip_output(ErlDrvData handle, char *buff, int bufflen) +static void trace_ip_output(ErlDrvData handle, char *buff, ErlDrvSizeT bufflen) { TraceIpData *data = (TraceIpData *) handle; if (data->flags & FLAG_LISTEN_PORT) { @@ -516,7 +518,7 @@ static void trace_ip_ready_output(ErlDrvData handle, ErlDrvEvent fd) tim = data->que[data->questart]; towrite = tim->siz - tim->written; while((res = write_until_done(data->fd, - tim->bin + tim->written, towrite)) + (char *)tim->bin + tim->written, towrite)) == towrite) { driver_free(tim); data->que[data->questart] = NULL; @@ -548,9 +550,10 @@ static void trace_ip_ready_output(ErlDrvData handle, ErlDrvEvent fd) /* ** Control message from erlang, we handle $p, which is get_listen_port. */ -static int trace_ip_control(ErlDrvData handle, unsigned int command, - char* buff, int count, - char** res, int res_size) +static ErlDrvSSizeT trace_ip_control(ErlDrvData handle, + unsigned int command, + char* buff, ErlDrvSizeT count, + char** res, ErlDrvSizeT res_size) { register void *void_ptr; /* Soft type cast */ @@ -558,7 +561,7 @@ static int trace_ip_control(ErlDrvData handle, unsigned int command, TraceIpData *data = (TraceIpData *) handle; ErlDrvBinary *b = my_alloc_binary(3); b->orig_bytes[0] = '\0'; /* OK */ - put_be16(data->listen_portno, &(b->orig_bytes[1])); + put_be16(data->listen_portno, (unsigned char *)&(b->orig_bytes[1])); *res = void_ptr = b; return 0; } @@ -693,7 +696,7 @@ static TraceIpMessage *make_buffer(int datasiz, unsigned char op, ** Add message to que, discarding in a politically correct way... ** The FLAG_DROP_OLDEST is currently ingored... */ -static void enque_message(TraceIpData *data, unsigned char *buff, int bufflen, +static void enque_message(TraceIpData *data, char *buff, int bufflen, int byteswritten) { int diff = data->questop - data->questart; @@ -760,13 +763,13 @@ static void close_client(TraceIpData *data) ** Try to write a message from erlang directly (only called when que is empty ** and client is connected) */ -static int trywrite(TraceIpData *data, unsigned char *buff, int bufflen) +static int trywrite(TraceIpData *data, char *buff, int bufflen) { - unsigned char op[5]; + char op[5]; int res; op[0] = OP_BINARY; - put_be32(bufflen, op + 1); + put_be32(bufflen, (unsigned char *)op + 1); if ((res = write_until_done(data->fd, op, 5)) < 0) { close_client(data); @@ -790,7 +793,11 @@ static int trywrite(TraceIpData *data, unsigned char *buff, int bufflen) static SOCKET my_accept(SOCKET sock) { struct sockaddr_in sin; - int sin_size = sizeof(sin); +#ifdef HAVE_SOCKLEN_T + socklen_t sin_size = sizeof(sin); +#else + int sin_size = (int) sizeof(sin); +#endif return accept(sock, (struct sockaddr *) &sin, &sin_size); } diff --git a/lib/runtime_tools/doc/src/Makefile b/lib/runtime_tools/doc/src/Makefile index dbbae81cfe..c67f7bd1f6 100644 --- a/lib/runtime_tools/doc/src/Makefile +++ b/lib/runtime_tools/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# Copyright Ericsson AB 1999-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # Target Specs # ---------------------------------------------------- XML_APPLICATION_FILES = ref_man.xml -XML_REF3_FILES = dbg.xml erts_alloc_config.xml +XML_REF3_FILES = dbg.xml dyntrace.xml erts_alloc_config.xml XML_REF6_FILES = runtime_tools_app.xml XML_PART_FILES = part_notes.xml part_notes_history.xml diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml index f26789fa21..c7c5cd4ff0 100644 --- a/lib/runtime_tools/doc/src/dbg.xml +++ b/lib/runtime_tools/doc/src/dbg.xml @@ -316,7 +316,8 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard) c <v>Module = atom() | '_'</v> <v>Function = atom() | '_'</v> <v>Arity = integer() |'_'</v> - <v>MatchSpec = integer() | atom() | [] | match_spec()</v> + <v>MatchSpec = integer() | Built-inAlias | [] | match_spec()</v> + <v>Built-inAlias = x | c | cx</v> <v>MatchDesc = [MatchInfo]</v> <v>MatchInfo = {saved, integer()} | MatchNum</v> <v>MatchNum = {matched, node(), integer()} | {matched, node(), 0, RPCError}</v> @@ -349,8 +350,9 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard) c 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 built-in - aliases named with atoms (see also <c>ltp/0</c> below).</p> + "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> <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 @@ -528,6 +530,21 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard) c <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> + <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 + <c>cx</c> respectively). + Exception trace sets a trace which will show function names, + parameters, return values and exceptions thrown from functions. + Caller traces display function names, parameters and information + about which function called it. An example using a built-in alias:</p> + <pre> +(x@y)4> <input>dbg:tp(lists,sort,cx).</input> +{ok,[{matched,nonode@nohost,2},{saved,cx}]} +(x@y)4> <input>lists:sort([2,1]).</input> +(<0.32.0>) call lists:sort([2,1]) ({erl_eval,do_apply,5}) +(<0.32.0>) returned from lists:sort/1 -> [1,2] +[1,2]</pre> </desc> </func> <func> diff --git a/lib/runtime_tools/doc/src/dyntrace.xml b/lib/runtime_tools/doc/src/dyntrace.xml new file mode 100644 index 0000000000..5fc5530f25 --- /dev/null +++ b/lib/runtime_tools/doc/src/dyntrace.xml @@ -0,0 +1,209 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>1996</year><year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>dyntrace</title> + <prepared>Patrik Nyblom</prepared> + <responsible></responsible> + <docno>1</docno> + <approved>ETX/B/SFP (Kenneth Lundin)</approved> + <checked></checked> + <date>12-03-20</date> + <rev>A</rev> + <file>dyntrace.xml</file> + </header> + <module>dyntrace</module> + <modulesummary>Interface to dynamic tracing</modulesummary> + <description> + <p>This module implements interfaces to dynamic tracing, should such be compiled into the virtual machine. For a standard and/or commercial build, no dynamic tracing is available, in which case none of the functions in this module is usable or give any effect.</p> + <p>Should dynamic tracing be enabled in the current build, either by configuring with <c>./configure --with-dynamic-trace=dtrace</c> or with <c>./configure --with-dynamic-trace=systemtap</c>, the module can be used for two things:</p> + <list type="bulleted"> + <item>Trigger the user-probe <c>user_trace_i4s4</c> in the NIF library <c>dyntrace.so</c> by calling <c>dyntrace:p/{1,2,3,4,5,6,7,8}</c>.</item> + <item>Set a user specified tag that will be present in the trace messages of both the <c>efile_drv</c> and the user-probe mentioned above.</item> + </list> + <p>Both building with dynamic trace probes and using them is experimental and unsupported by Erlang/OTP. It is included as an option for the developer to trace and debug performance issues in their systems.</p> + <p>The original implementation is mostly done by Scott Lystiger Fritchie as an Open Source Contribution and it should be viewed as such even though the source for dynamic tracing as well as this module is included in the main distribution. However, the ability to use dynamic tracing of the virtual machine is a very valuable contribution which OTP has every intention to maintain as a tool for the developer.</p> + <p>How to write <c>d</c> programs or <c>systemtap</c> scripts can be learned from books and from a lot of pages on the Internet. This manual page does not include any documentation about using the dynamic trace tools of respective platform. The <c>examples</c> directory of the <c>runtime_tools</c> application however contains comprehensive examples of both <c>d</c> and <c>systemtap</c> programs that will help you get started. Another source of information is the <c>README.dtrace(.md)</c> and <c>README.systemtap(.md)</c> files in the Erlang source top directory.</p> + </description> + <funcs> + <func> + <name>available() -> boolean()</name> + <fsummary>Check if dynamic tracing is available</fsummary> + <desc> + <p>This function uses the NIF library to determine if dynamic + tracing is available. Usually calling <seealso + marker="erts:erlang#system_info/1">erlang:system_info/1</seealso> + is a better indicator of the availability of dynamic + tracing.</p> + <p>The function will throw an exception if the <c>dyntrace</c> NIF library could not be loaded by the on_load function of this module.</p> + </desc> + </func> + <func> + <name>p() -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message only containing the user tag and zeroes/empty strings in all other fields.</p> + </desc> + </func> + <func> + <name>p(integer() | string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer or string parameter in the first integer/string field.</p> + </desc> + </func> + <func> + <name>p(integer() | string(), integer() | string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters. I.e. <c>p(1,"Hello")</c> is ok, as is <c>p(1,1)</c> and <c>p("Hello","Again")</c>, but not <c>p("Hello",1)</c>.</p> + </desc> + </func> + <func> + <name>p(integer() | string(), integer() | string(), integer() | string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + </desc> + </func> + <func> + <name>p(integer() | string(), integer() | string(), integer() | string(), integer() | string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + </desc> + </func> + <func> + <name>p(integer(), integer() | string(), integer() | string(), integer() | string(), string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + <p>There can be no more than four parameters of any type (integer() or string()), so the first parameter has to be an integer() and the last a string().</p> + </desc> + </func> + <func> + <name>p(integer(), integer(), integer() | string(), integer() | string(), string(), string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + <p>There can be no more than four parameters of any type (integer() or string()), so the first two parameters has to be integer()'s and the last two string()'s.</p> + </desc> + </func> + <func> + <name>p(integer(), integer(), integer(), integer() | string(), string(), string(), string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + <p>There can be no more than four parameters of any type (integer() or string()), so the first three parameters has to be integer()'s and the last three string()'s.</p> + </desc> + </func> + <func> + <name>p(integer(), integer(), integer(), integer(), string(), string(), string(), string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing all the integer()'s and string()'s provided, as well as any user tag set in the current process.</p> + </desc> + </func> + <func> + <name>get_tag() -> binary() | undefined</name> + <fsummary>Get the user tag set in the process.</fsummary> + <desc> + <p>This function returns the user tag set in the current + process. If no tag is set or dynamic tracing is not available, + it returns <c>undefined</c></p> + </desc> + </func> + <func> + <name>get_tag() -> binary() | undefined</name> + <fsummary>Get the user tag set in the process or sent to the process.</fsummary> + <desc> + <p>This function returns the user tag set in the current + process or, if no user tag is present, the last user tag sent + to the process together with a message (in the same way as + <seealso marker="kernel:seq_trace">sequential trace + tokens</seealso> are spread to other processes together with + messages. For an explanation of how user tags can be spread + together with messages, see <seealso + marker="#spread_tag/1">spread_tag/1</seealso>. If no tag is + found or dynamic tracing is not available, it returns + <c>undefined</c></p> + </desc> + </func> + + <func> + <name>put_tag(Item) -> binary() | undefined </name> + <fsummary>Set the user tag of the current process.</fsummary> + <type> + <v>Item = iodata()</v> + </type> + <desc> + <p>This function sets the user tag of the current process. The user tag is a binary(), but can be specified as any iodata(), which is automatically converted to a binary by this function.</p> + <p>The user tag is provided to the user probes triggered by calls top <c>dyntrace:p/{1,2,3,4,5,6,7,8}</c> as well as probes in the efile_driver. In the future, user tags might be added to more probes.</p> + <p>The old user tag (if any) is returned, or <c>undefined</c> if no user tag was present or dynamic tracing is not enabled.</p> + </desc> + </func> + <func> + <name>spread_tag(boolean()) -> TagData</name> + <fsummary>Start or stop spreading dynamic trace user tags with the next message.</fsummary> + <type> + <v>TagData = opaque data that can be used as parameter to <seealso marker="#restore_tag/1">restore_tag/1</seealso></v> + </type> + <desc> + <p>This function controls if user tags are to be spread to other processes with the next message. Spreading of user tags work like spreading of sequential trace tokens, so that a received user tag will be active in the process until the next message arrives (if that message does not also contain the user tag.</p> + <p>This functionality is used when a client process communicates with a file i/o-server to spread the user tag to the I/O-server and then down to the efile_drv driver. By using <c>spread_tag/1</c> and <c>restore_tag/1</c>, one can enable or disable spreading of user tags to other processes and then restore the previous state of the user tag. The TagData returned from this call contains all previous information so the state (including any previously spread user tags) will be completely restored by a later call to <c>restore_tag/1</c>.</p> + <p>The <seealso marker="kernel:file">file</seealso> module already spread's tags, so there is noo need to manually call these function to get user tags spread to the efile driver through that module.</p> + <p>The most use of this function would be if one for example uses the <seealso marker="stdlib:io">io</seealso> module to communicate with an I/O-server for a regular file, like in the following example:</p> +<pre> +f() -> + {ok, F} = file:open("test.tst",[write]), + Saved = dyntrace:spread_tag(true), + io:format(F,"Hello world!",[]), + dyntrace:restore_tag(Saved), + file:close(F). +</pre> + <p>In this example, any user tag set in the calling process will be spread to the I/O-server when the io:format call is done.</p> + </desc> + </func> + <func> + <name>restore_tag(TagData) -> true</name> + <fsummary>Restore to a previous state of user tag spreading.</fsummary> + <type> + <v>TagData = opaque data returned by <seealso marker="#spread_tag/1">spread_tag/1</seealso></v> + </type> + <desc> + <p>Restores the previous state of user tags and their spreading as it was before a call to <seealso marker="#spread_tag/1">spread_tag/1</seealso>. Note that the restoring is not limited to the same process, one can utilize this to turn off spreding in one process and restore it in a newly created, the one that actually is going to send messages:</p> +<pre> +f() -> + TagData=dyntrace:spread_tag(false), + spawn(fun() -> + dyntrace:restore_tag(TagData), + do_something() + end), + do_something_else(), + dyntrace:restore_tag(TagData). +</pre> + <p>Correctly handling user tags and their spreading might take some effort, as Erlang programs tend to send and receive messages so that sometimes the user tag gets lost due to various things, like double receives or communication with a port (ports do not handle user tags, in the same way as they do not handle regular sequential trace tokens).</p> + </desc> + </func> + </funcs> + </erlref> + diff --git a/lib/runtime_tools/doc/src/make.dep b/lib/runtime_tools/doc/src/make.dep deleted file mode 100644 index 85eae88adf..0000000000 --- a/lib/runtime_tools/doc/src/make.dep +++ /dev/null @@ -1,20 +0,0 @@ -# ---------------------------------------------------- -# >>>> Do not edit this file <<<< -# This file was automaticly generated by -# /home/otp/bin/docdepend -# ---------------------------------------------------- - - -# ---------------------------------------------------- -# TeX files that the DVI file depend on -# ---------------------------------------------------- - -book.dvi: book.tex dbg.tex erts_alloc_config.tex refman.tex \ - runtime_tools_app.tex - -# ---------------------------------------------------- -# Source inlined when transforming from source to LaTeX -# ---------------------------------------------------- - -book.tex: refman.xml - diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml index 0bb76e1ea4..2d4a206e1c 100644 --- a/lib/runtime_tools/doc/src/notes.xml +++ b/lib/runtime_tools/doc/src/notes.xml @@ -31,6 +31,78 @@ <p>This document describes the changes made to the Runtime_Tools application.</p> +<section><title>Runtime_Tools 1.8.8</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The DTrace source patch from Scott Lystig Fritchie is + integrated in the source tree. Using an emulator with + dtrace probe is still not supported for production use, + but may be a valuable debugging tool. Configure with + --with-dynamic-trace=dtrace (or + --with-dynamic-trace=systemtap) to create a build with + dtrace probes enabled. See runtime_tools for + documentation and examples.</p> + <p> + Own Id: OTP-10017</p> + </item> + </list> + </section> + +</section> + +<section><title>Runtime_Tools 1.8.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Earlier dbg:stop only did erlang:trace_delivered and did + not flush the trace file driver. Therefore there could + still be trace messages that were delivered to the driver + (guaranteed by erlang:trace_delivered) but not yet + written to the file when dbg:stop returned. Flushing is + now added on each node before the dbg process terminates.</p> + <p> + Own Id: OTP-9651</p> + </item> + <item> + <p> + File handles created by the trace_file_drv driver was + inherited to child processes. This is now corrected.</p> + <p> + Own Id: OTP-9658</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Erlang/OTP can now be built using parallel make if you + limit the number of jobs, for instance using '<c>make + -j6</c>' or '<c>make -j10</c>'. '<c>make -j</c>' does not + work at the moment because of some missing + dependencies.</p> + <p> + Own Id: OTP-9451</p> + </item> + <item> + <p> + Two new built-in trace pattern aliases have been added: + caller_trace (c) and caller_exception_trace (cx). See the + dbg:ltp/0 documentation for more info.</p> + <p> + Own Id: OTP-9458</p> + </item> + </list> + </section> + +</section> + <section><title>Runtime_Tools 1.8.6</title> <section><title>Improvements and New Features</title> diff --git a/lib/runtime_tools/doc/src/ref_man.xml b/lib/runtime_tools/doc/src/ref_man.xml index 579efcd969..df3446cd17 100644 --- a/lib/runtime_tools/doc/src/ref_man.xml +++ b/lib/runtime_tools/doc/src/ref_man.xml @@ -4,7 +4,7 @@ <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1999</year><year>2009</year> + <year>1999</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -33,6 +33,7 @@ </description> <xi:include href="runtime_tools_app.xml"/> <xi:include href="dbg.xml"/> + <xi:include href="dyntrace.xml"/> <xi:include href="erts_alloc_config.xml"/> </application> diff --git a/lib/runtime_tools/examples/dist.d b/lib/runtime_tools/examples/dist.d new file mode 100644 index 0000000000..7e2d7f0e35 --- /dev/null +++ b/lib/runtime_tools/examples/dist.d @@ -0,0 +1,62 @@ +/* example usage: dtrace -q -s /path/to/dist.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::dist-monitor +{ + printf("monitor: pid %d, who %s, what %s, node %s, type %s, reason %s\n", + pid, + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3), + copyinstr(arg4)); +} + +erlang*:::dist-port_busy +{ + printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); + /* + * For variable use advice, see: + * http://dtrace.org/blogs/brendan/2011/11/25/dtrace-variable-types/ + * + * Howevever, it's quite possible for the blocked events to span + * threads, so we'll use globals. + */ + blocked_procs[copyinstr(arg3)] = timestamp; +} + +erlang*:::dist-output +{ + printf("dist output: node %s, port %s, remote_node %s bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::dist-outputv +{ + printf("port outputv: node %s, port %s, remote_node %s bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::process-scheduled +/blocked_procs[copyinstr(arg0)]/ +{ + pidstr = copyinstr(arg0); + printf("blocked pid %s scheduled now, waited %d microseconds\n", + pidstr, (timestamp - blocked_procs[pidstr]) / 1000); + blocked_procs[pidstr] = 0; +} diff --git a/lib/runtime_tools/examples/dist.systemtap b/lib/runtime_tools/examples/dist.systemtap new file mode 100644 index 0000000000..8935e19e28 --- /dev/null +++ b/lib/runtime_tools/examples/dist.systemtap @@ -0,0 +1,76 @@ +/* example usage: stap /path/to/dist.systemtap -x <pid> */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("dist-monitor") +{ + printf("monitor: pid %d, who %s, what %s, node %s, type %s, reason %s\n", + pid(), + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4), + user_string($arg5)); +} + +probe process("beam").mark("dist-port_busy") +{ + printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); + blocked_procs[user_string($arg4)] = timestamp; +} + +probe process("beam").mark("dist-port_busy") +{ + printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); + blocked_procs[user_string($arg4)] = timestamp; +} + +probe process("beam").mark("dist-output") +{ + printf("dist output: node %s, port %s, remote_node %s bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); +} + +probe process("beam").mark("dist-outputv") +{ + printf("port outputv: node %s, port %s, remote_node %s bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); +} + +probe process("beam").mark("process-scheduled") +{ + pidstr = user_string($arg1); + if (pidstr in blocked_procs) { + printf("blocked pid %s scheduled now, waited %d microseconds\n", + pidstr, (timestamp - blocked_procs[pidstr]) / 1000); + delete blocked_procs[pidstr]; + } +} + +global blocked_procs; diff --git a/lib/runtime_tools/examples/driver1.d b/lib/runtime_tools/examples/driver1.d new file mode 100644 index 0000000000..4871a8ee69 --- /dev/null +++ b/lib/runtime_tools/examples/driver1.d @@ -0,0 +1,114 @@ +/* example usage: dtrace -q -s /path/to/driver1.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::driver-init +{ + printf("driver init name %s major %d minor %d flags %d\n", + copyinstr(arg0), arg1, arg2, arg3); +} + +erlang*:::driver-start +{ + printf("driver start pid %s driver name %s port %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-stop +{ + printf("driver stop pid %s driver name %s port %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-finish +{ + printf("driver finish driver name %s port %s\n", + copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::driver-flush +{ + printf("driver flush pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-output +{ + printf("driver output pid %s port %s port name %s bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::driver-outputv +{ + printf("driver outputv pid %s port %s port name %s bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::driver-control +{ + printf("driver control pid %s port %s port name %s command %d bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3, arg4); +} + +erlang*:::driver-call +{ + printf("driver call pid %s port %s port name %s command %d bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3, arg4); +} + +erlang*:::driver-event +{ + printf("driver event pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-ready_input +{ + printf("driver ready_input pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-ready_output +{ + printf("driver ready_output pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-timeout +{ + printf("driver timeout pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-ready_async +{ + printf("driver ready_async pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-process_exit +{ + printf("driver process_exit pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-stop_select +{ + printf("driver stop_select driver name %s\n", copyinstr(arg0)); +} diff --git a/lib/runtime_tools/examples/driver1.systemtap b/lib/runtime_tools/examples/driver1.systemtap new file mode 100644 index 0000000000..deae82f8fb --- /dev/null +++ b/lib/runtime_tools/examples/driver1.systemtap @@ -0,0 +1,125 @@ +/* example usage: stap /path/to/driver1.systemtap -x <pid> */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("driver-init") +{ + printf("driver init name %s major %d minor %d flags %d\n", + user_string($arg1), $arg2, $arg3, $arg4); +} + +probe process("beam").mark("driver-start") +{ + printf("driver start pid %s driver name %s port %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-stop") +{ + printf("driver stop pid %s driver name %s port %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-finish") +{ + printf("driver finish driver name %s\n", + user_string($arg1)); +} + +probe process("beam").mark("driver-flush") +{ + printf("driver flush pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-output") +{ + printf("driver output pid %s port %s port name %s bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); +} + +probe process("beam").mark("driver-outputv") +{ + printf("driver outputv pid %s port %s port name %s bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); +} + +probe process("beam").mark("driver-control") +{ + printf("driver control pid %s port %s port name %s command %d bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4, $arg5); +} + +probe process("beam").mark("driver-call") +{ + printf("driver call pid %s port %s port name %s command %d bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4, $arg5); +} + +probe process("beam").mark("driver-event") +{ + printf("driver event pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-ready_input") +{ + printf("driver ready_input pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-ready_output") +{ + printf("driver ready_output pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-timeout") +{ + printf("driver timeout pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-ready_async") +{ + printf("driver ready_async pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-process_exit") +{ + printf("driver process_exit pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-stop_select") +{ + printf("driver stop_select driver name %s\n", user_string($arg1)); +} diff --git a/lib/runtime_tools/examples/efile_drv.d b/lib/runtime_tools/examples/efile_drv.d new file mode 100644 index 0000000000..2442222552 --- /dev/null +++ b/lib/runtime_tools/examples/efile_drv.d @@ -0,0 +1,104 @@ +/* example usage: dtrace -q -s /path/to/efile_drv.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +BEGIN +{ + op_map[1] = "OPEN"; + op_map[2] = "READ"; + op_map[3] = "LSEEK"; + op_map[4] = "WRITE"; + op_map[5] = "FSTAT"; + op_map[6] = "PWD"; + op_map[7] = "READDIR"; + op_map[8] = "CHDIR"; + op_map[9] = "FSYNC"; + op_map[10] = "MKDIR"; + op_map[11] = "DELETE"; + op_map[12] = "RENAME"; + op_map[13] = "RMDIR"; + op_map[14] = "TRUNCATE"; + op_map[15] = "READ_FILE"; + op_map[16] = "WRITE_INFO"; + op_map[19] = "LSTAT"; + op_map[20] = "READLINK"; + op_map[21] = "LINK"; + op_map[22] = "SYMLINK"; + op_map[23] = "CLOSE"; + op_map[24] = "PWRITEV"; + op_map[25] = "PREADV"; + op_map[26] = "SETOPT"; + op_map[27] = "IPREAD"; + op_map[28] = "ALTNAME"; + op_map[29] = "READ_LINE"; + op_map[30] = "FDATASYNC"; + op_map[31] = "FADVISE"; +} + +erlang*:::aio_pool-add +{ + printf("async I/O pool port %s queue len %d\n", copyinstr(arg0), arg1); +} + +erlang*:::aio_pool-get +{ + printf("async I/O pool port %s queue len %d\n", copyinstr(arg0), arg1); +} + +erlang*:::efile_drv-entry +{ + printf("efile_drv enter tag={%d,%d} %s%s | %s (%d) | args: %s %s , %d %d (port %s)\n", + arg0, arg1, + arg2 == NULL ? "" : "user tag ", + arg2 == NULL ? "" : copyinstr(arg2), + op_map[arg3], arg3, + arg4 == NULL ? "" : copyinstr(arg4), + arg5 == NULL ? "" : copyinstr(arg5), arg6, arg7, + /* NOTE: port name in args[10] is experimental */ + (args[10] == NULL) ? + "?" : copyinstr((user_addr_t) args[10])); +} + +erlang*:::efile_drv-int* +{ + printf("async I/O worker tag={%d,%d} | %s (%d) | %s\n", + arg0, arg1, op_map[arg2], arg2, probename); +} + +/* efile_drv-return error case */ +erlang*:::efile_drv-return +/arg4 == 0/ +{ + printf("efile_drv return tag={%d,%d} %s%s | %s (%d) | errno %d\n", + arg0, arg1, + arg2 == NULL ? "" : "user tag ", + arg2 == NULL ? "" : copyinstr(arg2), + op_map[arg3], arg3, + arg5); +} + +/* efile_drv-return success case */ +erlang*:::efile_drv-return +/arg4 != 0/ +{ + printf("efile_drv return tag={%d,%d} %s | %s (%d) ok\n", + arg0, arg1, + arg2 == NULL ? "" : copyinstr(arg2), + op_map[arg3], arg3); +} diff --git a/lib/runtime_tools/examples/efile_drv.systemtap b/lib/runtime_tools/examples/efile_drv.systemtap new file mode 100644 index 0000000000..4b92e67048 --- /dev/null +++ b/lib/runtime_tools/examples/efile_drv.systemtap @@ -0,0 +1,112 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe begin +{ + op_map[1] = "OPEN"; + op_map[2] = "READ"; + op_map[3] = "LSEEK"; + op_map[4] = "WRITE"; + op_map[5] = "FSTAT"; + op_map[6] = "PWD"; + op_map[7] = "READDIR"; + op_map[8] = "CHDIR"; + op_map[9] = "FSYNC"; + op_map[10] = "MKDIR"; + op_map[11] = "DELETE"; + op_map[12] = "RENAME"; + op_map[13] = "RMDIR"; + op_map[14] = "TRUNCATE"; + op_map[15] = "READ_FILE"; + op_map[16] = "WRITE_INFO"; + op_map[19] = "LSTAT"; + op_map[20] = "READLINK"; + op_map[21] = "LINK"; + op_map[22] = "SYMLINK"; + op_map[23] = "CLOSE"; + op_map[24] = "PWRITEV"; + op_map[25] = "PREADV"; + op_map[26] = "SETOPT"; + op_map[27] = "IPREAD"; + op_map[28] = "ALTNAME"; + op_map[29] = "READ_LINE"; + op_map[30] = "FDATASYNC"; + op_map[31] = "FADVISE"; +} + +probe process("beam").mark("aio_pool-add") +{ + printf("async I/O pool port %s queue len %d\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("aio_pool-get") +{ + printf("async I/O pool port %s queue len %d\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("efile_drv-entry") +{ + printf("efile_drv enter tag={%d,%d} %s%s | %s (%d) | args: %s %s , %d %d (port %s)\n", + $arg1, $arg2, + $arg3 == NULL ? "" : "user tag ", + $arg3 == NULL ? "" : user_string($arg3), + op_map[$arg4], $arg4, + $arg5 == NULL ? "" : user_string($arg5), + $arg6 == NULL ? "" : user_string($arg6), $arg7, $arg8, + /* NOTE: port name in $arg[11] is experimental */ + user_string($arg11)) +} + +probe process("beam").mark("efile_drv-int*") +{ + printf("async I/O worker tag={%d,%d} | %s (%d) | %s\n", + $arg1, $arg2, op_map[$arg3], $arg3, probefunc()); +} + +probe process("beam").mark("efile_drv-return") +{ + if ($arg5 == 0) { + /* efile_drv-return error case */ + printf("efile_drv return tag={%d,%d} %s%s | %s (%d) | errno %d\n", + $arg1, $arg2, + $arg3 == NULL ? "" : "user tag ", + $arg3 == NULL ? "" : user_string($arg3), + op_map[$arg4], $arg4, + $arg6); + } else { + /* efile_drv-return success case */ + printf("efile_drv return tag={%d,%d} %s | %s (%d) ok\n", + $arg1, $arg2, + $arg3 == NULL ? "" : user_string($arg3), + op_map[$arg4], $arg4); + } +} + +global op_map; diff --git a/lib/runtime_tools/examples/function-calls.d b/lib/runtime_tools/examples/function-calls.d new file mode 100644 index 0000000000..ec6090553e --- /dev/null +++ b/lib/runtime_tools/examples/function-calls.d @@ -0,0 +1,57 @@ +/* example usage: dtrace -q -s /path/to/function-calls.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::local-function-entry +{ + printf("pid %s enter (local) %s depth %d\n", + copyinstr(arg0), copyinstr(arg1), arg2); +} + +erlang*:::global-function-entry +{ + printf("pid %s enter (global) %s depth %d\n", + copyinstr(arg0), copyinstr(arg1), arg2); +} + +erlang*:::function-return +{ + printf("pid %s return %s depth %d\n", + copyinstr(arg0), copyinstr(arg1), arg2); +} + +erlang*:::bif-entry +{ + printf("pid %s BIF entry mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::bif-return +{ + printf("pid %s BIF return mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::nif-entry +{ + printf("pid %s NIF entry mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::nif-return +{ + printf("pid %s NIF return mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} diff --git a/lib/runtime_tools/examples/function-calls.systemtap b/lib/runtime_tools/examples/function-calls.systemtap new file mode 100644 index 0000000000..4de54cfb5a --- /dev/null +++ b/lib/runtime_tools/examples/function-calls.systemtap @@ -0,0 +1,67 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("local-function-entry") +{ + printf("pid %s enter (local) %s depth %d\n", + user_string($arg1), user_string($arg2), $arg3); +} + +probe process("beam").mark("global-function-entry") +{ + printf("pid %s enter (global) %s depth %d\n", + user_string($arg1), user_string($arg2), $arg3); +} + +probe process("beam").mark("function-return") +{ + printf("pid %s return %s depth %d\n", + user_string($arg1), user_string($arg2), $arg3); +} + +probe process("beam").mark("bif-entry") +{ + printf("pid %s BIF entry mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("bif-return") +{ + printf("pid %s BIF return mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("nif-entry") +{ + printf("pid %s NIF entry mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("nif-return") +{ + printf("pid %s NIF return mfa %s\n", user_string($arg1), user_string($arg2)); +} diff --git a/lib/runtime_tools/examples/garbage-collection.d b/lib/runtime_tools/examples/garbage-collection.d new file mode 100644 index 0000000000..ebc40a19ec --- /dev/null +++ b/lib/runtime_tools/examples/garbage-collection.d @@ -0,0 +1,39 @@ +/* example usage: dtrace -q -s /path/to/garbage-collection.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::gc_major-start +{ + printf("GC major start pid %s need %d words\n", copyinstr(arg0), arg1); +} + +erlang*:::gc_minor-start +{ + printf("GC minor start pid %s need %d words\n", copyinstr(arg0), arg1); +} + +erlang*:::gc_major-end +{ + printf("GC major end pid %s reclaimed %d words\n", copyinstr(arg0), arg1); +} + +erlang*:::gc_minor-start +{ + printf("GC minor end pid %s reclaimed %d words\n", copyinstr(arg0), arg1); +} diff --git a/lib/runtime_tools/examples/garbage-collection.systemtap b/lib/runtime_tools/examples/garbage-collection.systemtap new file mode 100644 index 0000000000..81e9d38196 --- /dev/null +++ b/lib/runtime_tools/examples/garbage-collection.systemtap @@ -0,0 +1,49 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("gc_major-start") +{ + printf("GC major start pid %s need %d words\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("gc_minor-start") +{ + printf("GC minor start pid %s need %d words\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("gc_major-end") +{ + printf("GC major end pid %s reclaimed %d words\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("gc_minor-start") +{ + printf("GC minor end pid %s reclaimed %d words\n", user_string($arg1), $arg2); +} diff --git a/lib/runtime_tools/examples/memory1.d b/lib/runtime_tools/examples/memory1.d new file mode 100644 index 0000000000..b222aeae62 --- /dev/null +++ b/lib/runtime_tools/examples/memory1.d @@ -0,0 +1,41 @@ +/* example usage: dtrace -q -s /path/to/memory1.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::copy-struct +{ + printf("copy_struct %d bytes\n", arg0); +} + +erlang*:::copy-object +{ + printf("copy_object pid %s %d bytes\n", copyinstr(arg0), arg1); +} + +erlang*:::process-heap_grow +{ + printf("proc heap grow pid %s %d -> %d bytes\n", copyinstr(arg0), + arg1, arg2); +} + +erlang*:::process-heap_shrink +{ + printf("proc heap shrink pid %s %d -> %d bytes\n", copyinstr(arg0), + arg1, arg2); +} diff --git a/lib/runtime_tools/examples/memory1.systemtap b/lib/runtime_tools/examples/memory1.systemtap new file mode 100644 index 0000000000..9374b97d18 --- /dev/null +++ b/lib/runtime_tools/examples/memory1.systemtap @@ -0,0 +1,51 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("copy-struct") +{ + printf("copy_struct %d bytes\n", $arg1); +} + +probe process("beam").mark("copy-object") +{ + printf("copy_object pid %s %d bytes\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("process-heap_grow") +{ + printf("proc heap grow pid %s %d -> %d bytes\n", user_string($arg1), + $arg2, $arg3); +} + +probe process("beam").mark("process-heap_shrink") +{ + printf("proc heap shrink pid %s %d -> %d bytes\n", user_string($arg1), + $arg2, $arg3); +} diff --git a/lib/runtime_tools/examples/messages.d b/lib/runtime_tools/examples/messages.d new file mode 100644 index 0000000000..08cf52f3d2 --- /dev/null +++ b/lib/runtime_tools/examples/messages.d @@ -0,0 +1,94 @@ +/* example usage: dtrace -q -s /path/to/messages.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +BEGIN +{ + printf("\n"); + printf("NOTE: message-queue message size 4294967295 means an external\n"); + printf(" message that the code isn't smart enough to determine\n"); + printf(" the actual size.\n"); + printf("\n"); +} + +erlang*:::message-send +/arg3 == 0 && arg4 == 0 && arg5 == 0/ +{ + printf("send: %s -> %s: %d words\n", + copyinstr(arg0), copyinstr(arg1), arg2); +} + +erlang*:::message-send +/arg3 != 0 || arg4 != 0 || arg5 != 0/ +{ + printf("send: %s label %d token {%d,%d} -> %s: %d words\n", + copyinstr(arg0), + arg3, arg4, arg5, + copyinstr(arg1), arg2); +} + +/* + * TODO: + * Weird, on my OS X box, beam says arg6 = 0 but this script says 4294967296. + */ + +erlang*:::message-send-remote +/arg4 == 0 && arg5 == 0 && (arg6 == 0 || arg6 >= 4294967296)/ +{ + printf("send : %s -> %s %s: %d words\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::message-send-remote +/arg4 != 0 || arg5 != 0 || arg6 < 4294967296/ +{ + printf("send : %s label %d token {%d,%d} -> %s %s: %d words\n", + copyinstr(arg0), + arg4, arg5, arg6, + copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::message-queued +/arg3 == 0 && arg4 == 0 && arg5 == 0/ +{ + printf("queued: %s: %d words, queue len %d\n", copyinstr(arg0), arg1, arg2); +} + +erlang*:::message-queued +/arg3 != 0 || arg4 != 0 || arg5 != 0/ +{ + printf("queued: %s label %d token {%d,%d}: %d words, queue len %d\n", + copyinstr(arg0), arg3, arg4, arg5, + arg1, arg2); +} + +erlang*:::message-receive +/arg3 == 0 && arg4 == 0 && arg5 == 0/ +{ + printf("receive: %s: %d words, queue len %d\n", + copyinstr(arg0), arg1, arg2); +} + +erlang*:::message-receive +/arg3 != 0 || arg4 != 0 || arg5 != 0/ +{ + printf("receive: %s label %d token {%d,%d}: %d words, queue len %d\n", + copyinstr(arg0), arg3, arg4, arg5, + arg1, arg2); +} diff --git a/lib/runtime_tools/examples/messages.systemtap b/lib/runtime_tools/examples/messages.systemtap new file mode 100644 index 0000000000..c58e1168f9 --- /dev/null +++ b/lib/runtime_tools/examples/messages.systemtap @@ -0,0 +1,87 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe begin +{ + printf("\n"); + printf("NOTE: message-queue message size 4294967295 means an external\n"); + printf(" message that the code isn't smart enough to determine\n"); + printf(" the actual size.\n"); + printf("\n"); +} + +probe process("beam").mark("message-send") +{ + if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { + printf("send: %s -> %s: %d words\n", + user_string($arg1), user_string($arg2), $arg3); + } else { + printf("send: %s label %d token {%d,%d} -> %s: %d words\n", + user_string($arg1), + $arg4, $arg5, $arg6, + user_string($arg2), $arg3); + } +} + +probe process("beam").mark("message-send-remote") +{ + if ($arg5 == 0 && $arg6 == 0 && $arg7 == 0) { + printf("send : %s -> %s %s: %d words\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); + } else { + printf("send : %s label %d token {%d,%d} -> %s %s: %d words\n", + user_string($arg1), + $arg5, $arg6, $arg7, + user_string($arg2), user_string($arg3), $arg4); + } +} + +probe process("beam").mark("message-queued") +{ + if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { + printf("queued: %s: %d words, queue len %d\n", user_string($arg1), $arg2, $arg3); + } else { + printf("queued: %s label %d token {%d,%d}: %d words, queue len %d\n", + user_string($arg1), $arg4, $arg5, $arg6, + $arg2, $arg3); + } +} + +probe process("beam").mark("message-receive") +{ + if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { + printf("receive: %s: %d words, queue len %d\n", + user_string($arg1), $arg2, $arg3); + } else { + printf("receive: %s label %d token {%d,%d}: %d words, queue len %d\n", + user_string($arg1), $arg4, $arg5, $arg6, + $arg2, $arg3); + } +} diff --git a/lib/runtime_tools/examples/port1.d b/lib/runtime_tools/examples/port1.d new file mode 100644 index 0000000000..1e3bd3d8a9 --- /dev/null +++ b/lib/runtime_tools/examples/port1.d @@ -0,0 +1,142 @@ +/* example usage: dtrace -q -s /path/to/port1.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +BEGIN +{ + driver_map["tcp_inet", 1] = "OPEN"; + driver_map["tcp_inet", 2] = "CLOSE"; + driver_map["tcp_inet", 3] = "CONNECT"; + driver_map["tcp_inet", 4] = "PEER"; + driver_map["tcp_inet", 5] = "NAME"; + driver_map["tcp_inet", 6] = "BIND"; + driver_map["tcp_inet", 7] = "SETOPTS"; + driver_map["tcp_inet", 8] = "GETOPTS"; + driver_map["tcp_inet", 11] = "GETSTAT"; + driver_map["tcp_inet", 12] = "GETHOSTNAME"; + driver_map["tcp_inet", 13] = "FDOPEN"; + driver_map["tcp_inet", 14] = "GETFD"; + driver_map["tcp_inet", 15] = "GETTYPE"; + driver_map["tcp_inet", 16] = "GETSTATUS"; + driver_map["tcp_inet", 17] = "GETSERVBYNAME"; + driver_map["tcp_inet", 18] = "GETSERVBYPORT"; + driver_map["tcp_inet", 19] = "SETNAME"; + driver_map["tcp_inet", 20] = "SETPEER"; + driver_map["tcp_inet", 21] = "GETIFLIST"; + driver_map["tcp_inet", 22] = "IFGET"; + driver_map["tcp_inet", 23] = "IFSET"; + driver_map["tcp_inet", 24] = "SUBSCRIBE"; + driver_map["tcp_inet", 25] = "GETIFADDRS"; + driver_map["tcp_inet", 40] = "ACCEPT"; + driver_map["tcp_inet", 41] = "LISTEN"; + driver_map["tcp_inet", 42] = "RECV"; + driver_map["tcp_inet", 43] = "UNRECV"; + driver_map["tcp_inet", 44] = "SHUTDOWN"; + driver_map["tcp_inet", 60] = "RECV"; + driver_map["tcp_inet", 61] = "LISTEN"; + driver_map["tcp_inet", 62] = "BINDX"; + /* No looping constructs, so repeat for udp_inet */ + driver_map["udp_inet", 1] = "OPEN"; + driver_map["udp_inet", 2] = "CLOSE"; + driver_map["udp_inet", 3] = "CONNECT"; + driver_map["udp_inet", 4] = "PEER"; + driver_map["udp_inet", 5] = "NAME"; + driver_map["udp_inet", 6] = "BIND"; + driver_map["udp_inet", 7] = "SETOPTS"; + driver_map["udp_inet", 8] = "GETOPTS"; + driver_map["udp_inet", 11] = "GETSTAT"; + driver_map["udp_inet", 12] = "GETHOSTNAME"; + driver_map["udp_inet", 13] = "FDOPEN"; + driver_map["udp_inet", 14] = "GETFD"; + driver_map["udp_inet", 15] = "GETTYPE"; + driver_map["udp_inet", 16] = "GETSTATUS"; + driver_map["udp_inet", 17] = "GETSERVBYNAME"; + driver_map["udp_inet", 18] = "GETSERVBYPORT"; + driver_map["udp_inet", 19] = "SETNAME"; + driver_map["udp_inet", 20] = "SETPEER"; + driver_map["udp_inet", 21] = "GETIFLIST"; + driver_map["udp_inet", 22] = "IFGET"; + driver_map["udp_inet", 23] = "IFSET"; + driver_map["udp_inet", 24] = "SUBSCRIBE"; + driver_map["udp_inet", 25] = "GETIFADDRS"; + driver_map["udp_inet", 40] = "ACCEPT"; + driver_map["udp_inet", 41] = "LISTEN"; + driver_map["udp_inet", 42] = "RECV"; + driver_map["udp_inet", 43] = "UNRECV"; + driver_map["udp_inet", 44] = "SHUTDOWN"; + driver_map["udp_inet", 60] = "RECV"; + driver_map["udp_inet", 61] = "LISTEN"; + driver_map["udp_inet", 62] = "BINDX"; +} + +erlang*:::port-open +{ + printf("port open pid %s port name %s port %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::port-command +{ + printf("port command pid %s port %s port name %s command type %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); +} + +erlang*:::port-control +{ + /* http://dtrace.org/blogs/brendan/2011/11/25/dtrace-variable-types/ */ + this->cmd = driver_map[copyinstr(arg2), arg3]; + this->cmd_str = (this->cmd == 0) ? "unknown" : this->cmd; + printf("port control pid %s port %s port name %s command %d %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3, + this->cmd_str); +} + +/* port-exit is fired as a result of port_close() or exit signal */ + +erlang*:::port-exit +{ + printf("port exit pid %s port %s port name %s reason %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); +} + +erlang*:::port-connect +{ + printf("port connect pid %s port %s port name %s new pid %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); +} + +erlang*:::port-busy +{ + printf("port busy %s\n", copyinstr(arg0)); +} + +erlang*:::port-not_busy +{ + printf("port not busy %s\n", copyinstr(arg0)); +} + +erlang*:::aio_pool-add +{ + printf("async I/O pool add thread %d queue len %d\n", arg0, arg1); +} + +erlang*:::aio_pool-get +{ + printf("async I/O pool get thread %d queue len %d\n", arg0, arg1); +} diff --git a/lib/runtime_tools/examples/port1.systemtap b/lib/runtime_tools/examples/port1.systemtap new file mode 100644 index 0000000000..c8af240d85 --- /dev/null +++ b/lib/runtime_tools/examples/port1.systemtap @@ -0,0 +1,152 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe begin +{ + driver_map["tcp_inet", 1] = "OPEN"; + driver_map["tcp_inet", 2] = "CLOSE"; + driver_map["tcp_inet", 3] = "CONNECT"; + driver_map["tcp_inet", 4] = "PEER"; + driver_map["tcp_inet", 5] = "NAME"; + driver_map["tcp_inet", 6] = "BIND"; + driver_map["tcp_inet", 7] = "SETOPTS"; + driver_map["tcp_inet", 8] = "GETOPTS"; + driver_map["tcp_inet", 11] = "GETSTAT"; + driver_map["tcp_inet", 12] = "GETHOSTNAME"; + driver_map["tcp_inet", 13] = "FDOPEN"; + driver_map["tcp_inet", 14] = "GETFD"; + driver_map["tcp_inet", 15] = "GETTYPE"; + driver_map["tcp_inet", 16] = "GETSTATUS"; + driver_map["tcp_inet", 17] = "GETSERVBYNAME"; + driver_map["tcp_inet", 18] = "GETSERVBYPORT"; + driver_map["tcp_inet", 19] = "SETNAME"; + driver_map["tcp_inet", 20] = "SETPEER"; + driver_map["tcp_inet", 21] = "GETIFLIST"; + driver_map["tcp_inet", 22] = "IFGET"; + driver_map["tcp_inet", 23] = "IFSET"; + driver_map["tcp_inet", 24] = "SUBSCRIBE"; + driver_map["tcp_inet", 25] = "GETIFADDRS"; + driver_map["tcp_inet", 40] = "ACCEPT"; + driver_map["tcp_inet", 41] = "LISTEN"; + driver_map["tcp_inet", 42] = "RECV"; + driver_map["tcp_inet", 43] = "UNRECV"; + driver_map["tcp_inet", 44] = "SHUTDOWN"; + driver_map["tcp_inet", 60] = "RECV"; + driver_map["tcp_inet", 61] = "LISTEN"; + driver_map["tcp_inet", 62] = "BINDX"; + /* No looping constructs, so repeat for udp_inet */ + driver_map["udp_inet", 1] = "OPEN"; + driver_map["udp_inet", 2] = "CLOSE"; + driver_map["udp_inet", 3] = "CONNECT"; + driver_map["udp_inet", 4] = "PEER"; + driver_map["udp_inet", 5] = "NAME"; + driver_map["udp_inet", 6] = "BIND"; + driver_map["udp_inet", 7] = "SETOPTS"; + driver_map["udp_inet", 8] = "GETOPTS"; + driver_map["udp_inet", 11] = "GETSTAT"; + driver_map["udp_inet", 12] = "GETHOSTNAME"; + driver_map["udp_inet", 13] = "FDOPEN"; + driver_map["udp_inet", 14] = "GETFD"; + driver_map["udp_inet", 15] = "GETTYPE"; + driver_map["udp_inet", 16] = "GETSTATUS"; + driver_map["udp_inet", 17] = "GETSERVBYNAME"; + driver_map["udp_inet", 18] = "GETSERVBYPORT"; + driver_map["udp_inet", 19] = "SETNAME"; + driver_map["udp_inet", 20] = "SETPEER"; + driver_map["udp_inet", 21] = "GETIFLIST"; + driver_map["udp_inet", 22] = "IFGET"; + driver_map["udp_inet", 23] = "IFSET"; + driver_map["udp_inet", 24] = "SUBSCRIBE"; + driver_map["udp_inet", 25] = "GETIFADDRS"; + driver_map["udp_inet", 40] = "ACCEPT"; + driver_map["udp_inet", 41] = "LISTEN"; + driver_map["udp_inet", 42] = "RECV"; + driver_map["udp_inet", 43] = "UNRECV"; + driver_map["udp_inet", 44] = "SHUTDOWN"; + driver_map["udp_inet", 60] = "RECV"; + driver_map["udp_inet", 61] = "LISTEN"; + driver_map["udp_inet", 62] = "BINDX"; +} + +probe process("beam").mark("port-open") +{ + printf("port open pid %s port name %s port %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("port-command") +{ + printf("port command pid %s port %s port name %s command type %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); +} + +probe process("beam").mark("port-control") +{ + cmd = driver_map[user_string($arg3), $arg4]; + cmd_str = (cmd == "") ? "unknown" : cmd; + printf("port control pid %s port %s port name %s command %d %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4, cmd_str); +} + +/* port-exit is fired as a result of port_close() or exit signal */ + +probe process("beam").mark("port-exit") +{ + printf("port exit pid %s port %s port name %s reason %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); +} + +probe process("beam").mark("port-connect") +{ + printf("port connect pid %s port %s port name %s new pid %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); +} + +probe process("beam").mark("port-busy") +{ + printf("port busy %s\n", user_string($arg1)); +} + +probe process("beam").mark("port-not_busy") +{ + printf("port not busy %s\n", user_string($arg1)); +} + +probe process("beam").mark("aio_pool-add") +{ + printf("async I/O pool add thread %d queue len %d\n", $arg1, $arg2); +} + +probe process("beam").mark("aio_pool-get") +{ + printf("async I/O pool get thread %d queue len %d\n", $arg1, $arg2); +} + +global driver_map;
\ No newline at end of file diff --git a/lib/runtime_tools/examples/process-scheduling.d b/lib/runtime_tools/examples/process-scheduling.d new file mode 100644 index 0000000000..6408d7d5f2 --- /dev/null +++ b/lib/runtime_tools/examples/process-scheduling.d @@ -0,0 +1,35 @@ +/* example usage: dtrace -q -s /path/to/process-scheduling.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::process-scheduled +{ + printf(" Schedule pid %s mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::process-unscheduled +{ + printf("Unschedule pid %s\n", copyinstr(arg0)); +} + +erlang*:::process-hibernate +{ + printf(" Hibernate pid %s resume mfa %s\n", + copyinstr(arg0), copyinstr(arg1)); +} diff --git a/lib/runtime_tools/examples/process-scheduling.systemtap b/lib/runtime_tools/examples/process-scheduling.systemtap new file mode 100644 index 0000000000..4cc3ef555c --- /dev/null +++ b/lib/runtime_tools/examples/process-scheduling.systemtap @@ -0,0 +1,45 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("process-scheduled") +{ + printf(" Schedule pid %s mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("process-unscheduled") +{ + printf("Unschedule pid %s\n", user_string($arg1)); +} + +probe process("beam").mark("process-hibernate") +{ + printf(" Hibernate pid %s resume mfa %s\n", + user_string($arg1), user_string($arg2)); +} diff --git a/lib/runtime_tools/examples/spawn-exit.d b/lib/runtime_tools/examples/spawn-exit.d new file mode 100644 index 0000000000..ae690b819a --- /dev/null +++ b/lib/runtime_tools/examples/spawn-exit.d @@ -0,0 +1,41 @@ +/* example usage: dtrace -q -s /path/to/spawn-exit.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::process-spawn +{ + printf("pid %s mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::process-exit +{ + printf("pid %s reason %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::process-exit_signal +{ + printf("sender %s -> pid %s reason %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::process-exit_signal-remote +{ + printf("sender %s -> node %s pid %s reason %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); +} diff --git a/lib/runtime_tools/examples/spawn-exit.systemtap b/lib/runtime_tools/examples/spawn-exit.systemtap new file mode 100644 index 0000000000..4b69e4aea6 --- /dev/null +++ b/lib/runtime_tools/examples/spawn-exit.systemtap @@ -0,0 +1,51 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("process-spawn") +{ + printf("pid %s mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("process-exit") +{ + printf("pid %s reason %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("process-exit_signal") +{ + printf("sender %s -> pid %s reason %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("process-exit_signal-remote") +{ + printf("sender %s -> node %s pid %s reason %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); +} diff --git a/lib/runtime_tools/examples/user-probe.d b/lib/runtime_tools/examples/user-probe.d new file mode 100644 index 0000000000..5cb5d61a76 --- /dev/null +++ b/lib/runtime_tools/examples/user-probe.d @@ -0,0 +1,36 @@ +/* example usage: dtrace -q -s /path/to/user-probe.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::user_trace-s1 +{ + printf("%s\n", copyinstr(arg0)); +} + +erlang*:::user_trace-i4s4 +{ + printf("%s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", + copyinstr(arg0), + arg1 == NULL ? "" : copyinstr(arg1), + arg2, arg3, arg4, arg5, + arg6 == NULL ? "" : copyinstr(arg6), + arg7 == NULL ? "" : copyinstr(arg7), + arg8 == NULL ? "" : copyinstr(arg8), + arg9 == NULL ? "" : copyinstr(arg9)); +} diff --git a/lib/runtime_tools/examples/user-probe.systemtap b/lib/runtime_tools/examples/user-probe.systemtap new file mode 100644 index 0000000000..b41d7483ad --- /dev/null +++ b/lib/runtime_tools/examples/user-probe.systemtap @@ -0,0 +1,46 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("dyntrace.so").mark("user_trace-s1") +{ + printf("%s\n", user_string($arg1)); +} + +probe process("dyntrace.so").mark("user_trace-i4s4") +{ + printf("%s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", + user_string($arg1), + $arg2 == NULL ? "" : user_string($arg2), + $arg3, $arg4, $arg5, $arg6, + $arg7 == NULL ? "" : user_string($arg7), + $arg8 == NULL ? "" : user_string($arg8), + $arg9 == NULL ? "" : user_string($arg9), + $arg9 == NULL ? "" : user_string($arg9)); +} diff --git a/lib/runtime_tools/src/Makefile b/lib/runtime_tools/src/Makefile index 4f831f3dd8..cb302b79c2 100644 --- a/lib/runtime_tools/src/Makefile +++ b/lib/runtime_tools/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# Copyright Ericsson AB 1999-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -45,8 +45,10 @@ MODULES= \ runtime_tools \ runtime_tools_sup \ dbg \ + dyntrace \ percept_profile \ - observer_backend + observer_backend \ + ttb_autostart HRL_FILES= ../include/observer_backend.hrl ERL_FILES= $(MODULES:%=%.erl) @@ -63,10 +65,15 @@ APPUP_FILE= runtime_tools.appup APPUP_SRC= $(APPUP_FILE).src APPUP_TARGET= $(EBIN)/$(APPUP_FILE) +EXAMPLE_FILES= \ + ../examples/* # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += -I../include +ERL_COMPILE_FLAGS += \ + -I../include \ + -I ../../et/include \ + -I ../../../libraries/et/include # ---------------------------------------------------- # Targets @@ -96,6 +103,8 @@ release_spec: opt $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src $(INSTALL_DIR) $(RELSYSDIR)/include $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) $(RELSYSDIR)/examples + $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples $(INSTALL_DIR) $(RELSYSDIR)/ebin $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl index 56283f4d3d..385047ee73 100644 --- a/lib/runtime_tools/src/dbg.erl +++ b/lib/runtime_tools/src/dbg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ -export([fun2ms/1]). %% Local exports --export([erlang_trace/3,get_info/0]). +-export([erlang_trace/3,get_info/0,deliver_and_flush/1]). %% Debug exports -export([wrap_presort/2, wrap_sort/2, wrap_postsort/1, wrap_sortfix/2, @@ -348,17 +348,16 @@ trace_port_control(Operation) -> trace_port_control(node(), Operation). trace_port_control(Node, flush) -> - Ref = erlang:trace_delivered(all), - receive - {trace_delivered,all,Ref} -> ok - end, - case trace_port_control(Node, $f, "") of - {ok, [0]} -> - ok; - {ok, _} -> - {error, not_supported_by_trace_driver}; - Other -> - Other + case get_tracer(Node) of + {ok, Port} when is_port(Port) -> + case catch rpc:call(Node,?MODULE,deliver_and_flush,[Port]) of + [0] -> + ok; + _ -> + {error, not_supported_by_trace_driver} + end; + _ -> + {error, no_trace_driver} end; trace_port_control(Node,get_listen_port) -> case trace_port_control(Node,$p, "") of @@ -378,7 +377,14 @@ trace_port_control(Node, Command, Arg) -> {error, no_trace_driver} end. - +%% A bit more than just flush - it also makes sure all trace messages +%% are delivered first, before flushing the driver. +deliver_and_flush(Port) -> + Ref = erlang:trace_delivered(all), + receive + {trace_delivered,all,Ref} -> ok + end, + erlang:port_control(Port, $f, ""). trace_port(file, {Filename, wrap, Tail}) -> @@ -684,18 +690,12 @@ loop({C,T}=SurviveLinks, Table) -> %% tracing on the node it removes from the list of active trace nodes, %% we will call erlang:trace_delivered/1 on ALL nodes that we have %% connections to. - Delivered = fun() -> - Ref = erlang:trace_delivered(all), - receive - {trace_delivered,all,Ref} -> ok - end - end, - catch rpc:multicall(nodes(), erlang, apply, [Delivered,[]]), - Ref = erlang:trace_delivered(all), - receive - {trace_delivered,all,Ref} -> - exit(done) - end; + %% If it is a file trace driver, we will also flush the port. + lists:foreach(fun({Node,{_Relay,Port}}) -> + rpc:call(Node,?MODULE,deliver_and_flush,[Port]) + end, + get()), + exit(done); {From, {link_to, Pid}} -> case (catch link(Pid)) of {'EXIT', Reason} -> @@ -1449,6 +1449,19 @@ new_pattern_table() -> ets:insert(PT, {exception_trace, term_to_binary(x)}), + ets:insert(PT, + {c, + term_to_binary([{'_',[],[{message,{caller}}]}])}), + ets:insert(PT, + {caller_trace, + term_to_binary(c)}), + ets:insert(PT, + {cx, + term_to_binary([{'_',[],[{exception_trace}, + {message,{caller}}]}])}), + ets:insert(PT, + {caller_exception_trace, + term_to_binary(cx)}), PT. diff --git a/lib/runtime_tools/src/dyntrace.erl b/lib/runtime_tools/src/dyntrace.erl new file mode 100644 index 0000000000..388c7679b9 --- /dev/null +++ b/lib/runtime_tools/src/dyntrace.erl @@ -0,0 +1,279 @@ +-module(dyntrace). + +%%% @doc The Dynamic tracing interface module +%%% +%%% This Dynamic tracing interface module, with the corresponding NIFs, should +%%% work on any operating system platform where user-space DTrace/Systemtap +%%% (and in the future LttNG UST) probes are supported. +%%% +%%% Use the `dyntrace:init()' function to load the NIF shared library and +%%% to initialize library's private state. +%%% +%%% It is recommended that you use the `dyntrace:p()' function to add +%%% Dynamic trace probes to your Erlang code. This function can accept up to +%%% four integer arguments and four string arguments; the integer +%%% argument(s) must come before any string argument. For example: +%%% ``` +%%% 1> dyntrace:put_tag("GGOOOAAALL!!!!!"). +%%% true +%%% 2> dyntrace:init(). +%%% ok +%%% +%%% % % % If using dtrace, enable the Dynamic trace probe using the 'dtrace' +%%% % % % command. +%%% +%%% 3> dyntrace:p(7, 8, 9, "one", "four"). +%%% true +%%% ''' +%%% +%%% Output from the example D script `user-probe.d' looks like: +%%% ``` +%%% <0.34.0> GGOOOAAALL!!!!! 7 8 9 0 'one' 'four' '' '' +%%% ''' +%%% +%%% If the expected type of variable is not present, e.g. integer when +%%% integer() is expected, or an I/O list when iolist() is expected, +%%% then the driver will ignore the user's input and use a default +%%% value of 0 or NULL, respectively. + +-export([available/0, + user_trace_s1/1, % TODO: unify with pid & tag args like user_trace_i4s4 + p/0, p/1, p/2, p/3, p/4, p/5, p/6, p/7, p/8]). +-export([put_tag/1, get_tag/0, get_tag_data/0, spread_tag/1, restore_tag/1]). + +-export([scaff/0]). % Development only +-export([user_trace_i4s4/9]). % Know what you're doing! +-on_load(on_load/0). + +-type probe_arg() :: integer() | iolist(). +-type int_p_arg() :: integer() | iolist() | undef. + +%% The *_maybe() types use atom() instead of a stricter 'undef' +%% because user_trace_i4s4/9 is exposed to the outside world, and +%% because the driver will allow any atom to be used as a "not +%% present" indication, we'll allow any atom in the types. + +-type integer_maybe() :: integer() | atom(). +-type iolist_maybe() :: iolist() | atom(). + +on_load() -> + PrivDir = code:priv_dir(runtime_tools), + LibName = "dyntrace", + Lib = filename:join([PrivDir, "lib", LibName]), + Status = case erlang:load_nif(Lib, 0) of + ok -> ok; + {error, {load_failed, _}}=Error1 -> + ArchLibDir = + filename:join([PrivDir, "lib", + erlang:system_info(system_architecture)]), + Candidate = + filelib:wildcard(filename:join([ArchLibDir,LibName ++ "*" ])), + case Candidate of + [] -> Error1; + _ -> + ArchLib = filename:join([ArchLibDir, LibName]), + erlang:load_nif(ArchLib, 0) + end; + Error1 -> Error1 + end, + case Status of + ok -> ok; + {error, {E, Str}} -> + case erlang:system_info(dynamic_trace) of + none -> + ok; + _ -> + error_logger:error_msg("Unable to load dyntrace library. Failed with error:~n +\"~p, ~s\"~n" + "Dynamic tracing is enabled but the driver is not built correctly~n",[ + E,Str]), + Status + end + end. + +%%% +%%% NIF placeholders +%%% + +-spec available() -> true | false. + +available() -> + erlang:nif_error(nif_not_loaded). + +-spec user_trace_s1(iolist()) -> true | false | error | badarg. + +user_trace_s1(_Message) -> + erlang:nif_error(nif_not_loaded). + +-spec user_trace_i4s4(iolist(), + integer_maybe(), integer_maybe(), + integer_maybe(), integer_maybe(), + iolist_maybe(), iolist_maybe(), + iolist_maybe(), iolist_maybe()) -> + true | false | error | badarg. + +user_trace_i4s4(_, _, _, _, _, _, _, _, _) -> + erlang:nif_error(nif_not_loaded). + +%%% +%%% Erlang support functions +%%% + +-spec p() -> true | false | error | badarg. + +p() -> + user_trace_int(undef, undef, undef, undef, undef, undef, undef, undef). + +-spec p(probe_arg()) -> true | false | error | badarg. + +p(I1) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, undef, undef, undef, undef); +p(S1) -> + user_trace_int(undef, undef, undef, undef, S1, undef, undef, undef). + +-spec p(probe_arg(), probe_arg()) -> true | false | error | badarg. + +p(I1, I2) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, undef, undef, undef, undef); +p(I1, S1) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, S1, undef, undef, undef); +p(S1, S2) -> + user_trace_int(undef, undef, undef, undef, S1, S2, undef, undef). + +-spec p(probe_arg(), probe_arg(), probe_arg()) -> true | false | error | badarg. + +p(I1, I2, I3) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, undef, undef, undef, undef); +p(I1, I2, S1) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, S1, undef, undef, undef); +p(I1, S1, S2) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, S1, S2, undef, undef); +p(S1, S2, S3) -> + user_trace_int(undef, undef, undef, undef, S1, S2, S3, undef). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, undef, undef, undef, undef); +p(I1, I2, I3, S1) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, S1, undef, undef, undef); +p(I1, I2, S1, S2) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, S1, S2, undef, undef); +p(I1, S1, S2, S3) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, S1, S2, S3, undef); +p(S1, S2, S3, S4) -> + user_trace_int(undef, undef, undef, undef, S1, S2, S3, S4). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4, S1) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, S1, undef, undef, undef); +p(I1, I2, I3, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, S1, S2, undef, undef); +p(I1, I2, S1, S2, S3) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, S1, S2, S3, undef); +p(I1, S1, S2, S3, S4) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, S1, S2, S3, S4). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, S1, S2, undef, undef); +p(I1, I2, I3, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, S1, S2, S3, undef); +p(I1, I2, S1, S2, S3, S4) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, S1, S2, S3, S4). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, S1, S2, S3, undef); +p(I1, I2, I3, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, S1, S2, S3, S4). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, S1, S2, S3, S4). + +-spec user_trace_int(int_p_arg(), int_p_arg(), int_p_arg(), int_p_arg(), + int_p_arg(), int_p_arg(), int_p_arg(), int_p_arg()) -> + true | false | error | badarg. + +user_trace_int(I1, I2, I3, I4, S1, S2, S3, S4) -> + UTag = get_tag(), + try + user_trace_i4s4(UTag, I1, I2, I3, I4, S1, S2, S3, S4) + catch + error:nif_not_loaded -> + false + end. + +-spec put_tag(undefined | iodata()) -> binary() | undefined. +put_tag(Data) -> + erlang:dt_put_tag(unicode:characters_to_binary(Data)). + +-spec get_tag() -> binary() | undefined. +get_tag() -> + erlang:dt_get_tag(). + +-spec get_tag_data() -> binary() | undefined. +%% Gets tag if set, otherwise the spread tag data from last incoming message +get_tag_data() -> + erlang:dt_get_tag_data(). + +-spec spread_tag(boolean()) -> true | {non_neg_integer(), binary() | []}. +%% Makes the tag behave as a sequential trace token, will spread with +%% messages to be picked up by someone using get_tag_data +spread_tag(B) -> + erlang:dt_spread_tag(B). + +-spec restore_tag(true | {non_neg_integer(), binary() | []}) -> true. +restore_tag(T) -> + erlang:dt_restore_tag(T). + + +%% Scaffolding to write tedious code: quick brute force and not 100% correct. + +scaff_int_args(N) -> + L = lists:sublist(["I1", "I2", "I3", "I4"], N), + [string:join(L, ", ")]. + +scaff_int_guards(N) -> + L = lists:sublist(["is_integer(I1)", "is_integer(I2)", "is_integer(I3)", + "is_integer(I4)"], N), + lists:flatten(string:join(L, ", ")). + +scaff_char_args(N) -> + L = lists:sublist(["S1", "S2", "S3", "S4"], N), + [string:join(L, ", ")]. + +scaff_fill(N) -> + [string:join(lists:duplicate(N, "undef"), ", ")]. + +scaff() -> + L = [begin + IntArgs = scaff_int_args(N_int), + IntGuards = scaff_int_guards(N_int), + IntFill = scaff_fill(4 - N_int), + CharArgs = scaff_char_args(N_char), + CharFill = scaff_fill(4 - N_char), + InArgs = string:join(IntArgs ++ CharArgs, ", "), + OutArgs = string:join(IntArgs ++ IntFill ++ CharArgs ++ CharFill, + ", "), + {N_int + N_char, + lists:flatten([io_lib:format("p(~s) when ~s ->\n", + [InArgs, IntGuards]), + io_lib:format(" user_trace_int(~s);\n", [OutArgs]) + ])} + end || N_int <- [0,1,2,3,4], N_char <- [0,1,2,3,4]], + [io:format("%%~p\n~s", [N, Str]) || {N, Str} <- lists:sort(L)]. diff --git a/lib/runtime_tools/src/erts_alloc_config.erl b/lib/runtime_tools/src/erts_alloc_config.erl index 0bcb202fd8..284e88d4a7 100644 --- a/lib/runtime_tools/src/erts_alloc_config.erl +++ b/lib/runtime_tools/src/erts_alloc_config.erl @@ -1,6 +1,7 @@ +%% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,6 +17,7 @@ %% The Initial Developer of the Original Code is Ericsson AB. %% %% %CopyrightEnd% +%% %%%------------------------------------------------------------------- %%% File : erts_alloc_config.erl @@ -71,6 +73,7 @@ A == binary_alloc; A == std_alloc; A == ets_alloc; + A == fix_alloc; A == eheap_alloc; A == ll_alloc; A == sl_alloc; @@ -94,6 +97,7 @@ [{binary_alloc, 131072}, {std_alloc, 131072}, {ets_alloc, 131072}, + {fix_alloc, 131072}, {eheap_alloc, 524288}, {ll_alloc, 2097152}, {sl_alloc, 131072}, @@ -104,6 +108,7 @@ [{binary_alloc, 10}, {std_alloc, 10}, {ets_alloc, 10}, + {fix_alloc, 10}, {eheap_alloc, 10}, {ll_alloc, 0}, {sl_alloc, 10}, @@ -438,9 +443,6 @@ conf_alloc(#conf{format_to = FTO} = Conf, #alloc{name = A} = Alc) -> chk_xnote(Conf, Alc). chk_xnote(#conf{format_to = FTO}, - #alloc{name = fix_alloc}) -> - fcp(FTO, "Cannot be configured."); -chk_xnote(#conf{format_to = FTO}, #alloc{name = sys_alloc}) -> fcp(FTO, "Cannot be configured. Default malloc implementation used."); chk_xnote(#conf{format_to = FTO}, @@ -470,7 +472,7 @@ au_conf_alloc(#conf{format_to = FTO} = Conf, _ -> fc(FTO, "~p instances used.", [Insts]), - format(FTO, " +M~ct ~p~n", [alloc_char(A), Insts]) + format(FTO, " +M~ct true~n", [alloc_char(A)]) end, mmbcs(Conf, Alc), smbcs_lmbcs_mmmbc(Conf, Alc), diff --git a/lib/runtime_tools/src/inviso_rt.erl b/lib/runtime_tools/src/inviso_rt.erl index ac7ac2a584..b162f5b045 100644 --- a/lib/runtime_tools/src/inviso_rt.erl +++ b/lib/runtime_tools/src/inviso_rt.erl @@ -2359,8 +2359,8 @@ list_wrapset(Prefix,Suffix) -> list_wrapset_2([File|Rest],RegExp) -> Length=length(File), - case regexp:first_match(File,RegExp) of - {match,1,Length} -> % This is a member of the set. + case re:run(File,RegExp) of + {match,[{0,Length}]} -> % This is a member of the set. [File|list_wrapset_2(Rest,RegExp)]; _ -> list_wrapset_2(Rest,RegExp) diff --git a/lib/runtime_tools/src/inviso_rt_lib.erl b/lib/runtime_tools/src/inviso_rt_lib.erl index 2c6964e53e..5dfe14068a 100644 --- a/lib/runtime_tools/src/inviso_rt_lib.erl +++ b/lib/runtime_tools/src/inviso_rt_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -197,15 +197,15 @@ match_modules(RegExpDir,RegExpMod,Actions) -> handle_expand_regexp_2([{Mod,Path}|Rest],RegExpDir,RegExpMod,Result) -> ModStr=atom_to_list(Mod), ModLen=length(ModStr), - case regexp:first_match(ModStr,RegExpMod) of - {match,1,ModLen} -> % Ok, The regexp matches the module. + case re:run(ModStr,RegExpMod) of + {match,[{0,ModLen}]} -> % Ok, The regexp matches the module. if is_list(RegExpDir),is_atom(Path) -> % Preloaded or covercompiled... handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result); is_list(RegExpDir),is_list(Path) -> % Dir reg-exp is used! PathOnly=filename:dirname(Path), % Must remove beam-file name. - case regexp:first_match(PathOnly,RegExpDir) of - {match,_,_} -> % Did find a match, that is enough! + case re:run(PathOnly,RegExpDir,[{capture,none}]) of + match -> % Did find a match, that is enough! handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,[Mod|Result]); _ -> % Either error or nomatch. handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result) @@ -233,8 +233,8 @@ handle_expand_regexp_3([Path|Rest],RegExpDir,RegExpMod,AllLoaded,Result) -> volumerelative -> % Only on Windows!? filename:absname(Path) end, - case regexp:first_match(AbsPath,RegExpDir) of - {match,_,_} -> % Ok, the directory is allowed. + case re:run(AbsPath,RegExpDir,[{capture,none}]) of + match -> % Ok, the directory is allowed. NewResult=handle_expand_regexp_3_1(Path,RegExpMod,AllLoaded,Result), handle_expand_regexp_3(Rest,RegExpDir,RegExpMod,AllLoaded,NewResult); _ -> % This directory does not qualify. @@ -262,8 +262,8 @@ handle_expand_regexp_3_2([File|Rest],RegExpMod,AllLoaded,Result) -> case {lists:keysearch(Mod,1,AllLoaded),lists:member(Mod,Result)} of {false,false} -> % This module is not tried before. ModLen=length(ModStr), - case regexp:first_match(ModStr,RegExpMod) of - {match,1,ModLen} -> % This module satisfies the regexp. + case re:run(ModStr,RegExpMod) of + {match,[{0,ModLen}]} -> % This module satisfies the regexp. handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,[Mod|Result]); _ -> % Error or not perfect match. handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,Result) diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl index 0f428de07a..01e99f3f5e 100644 --- a/lib/runtime_tools/src/observer_backend.erl +++ b/lib/runtime_tools/src/observer_backend.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -21,6 +21,9 @@ %% General -export([vsn/0]). +%% observer stuff +-export([sys_info/0, get_table/3, get_table_list/2, fetch_stats/2]). + %% etop stuff -export([etop_collect/1]). -include("observer_backend.hrl"). @@ -31,6 +34,7 @@ ttb_write_binary/2, ttb_stop/1, ttb_fetch/2, + ttb_resume_trace/0, ttb_get_filenames/1]). -define(CHUNKSIZE,8191). % 8 kbytes - 1 byte @@ -41,8 +45,175 @@ vsn() -> Error -> Error end. +%% +%% observer backend +%% +sys_info() -> + {{_,Input},{_,Output}} = erlang:statistics(io), + [{process_count, erlang:system_info(process_count)}, + {process_limit, erlang:system_info(process_limit)}, + {uptime, element(1, erlang:statistics(wall_clock))}, + {run_queue, erlang:statistics(run_queue)}, + {io_input, Input}, + {io_output, Output}, + {logical_processors, erlang:system_info(logical_processors)}, + {logical_processors_available, erlang:system_info(logical_processors_available)}, + {logical_processors_online, erlang:system_info(logical_processors_online)}, + + {otp_release, erlang:system_info(otp_release)}, + {version, erlang:system_info(version)}, + {system_architecture, erlang:system_info(system_architecture)}, + {kernel_poll, erlang:system_info(kernel_poll)}, + {smp_support, erlang:system_info(smp_support)}, + {threads, erlang:system_info(threads)}, + {thread_pool_size, erlang:system_info(thread_pool_size)}, + {wordsize_internal, erlang:system_info({wordsize, internal})}, + {wordsize_external, erlang:system_info({wordsize, external})} | + erlang:memory() + ]. + +get_table(Parent, Table, Module) -> + spawn(fun() -> + link(Parent), + get_table2(Parent, Table, Module) + end). + +get_table2(Parent, Table, Type) -> + Size = case Type of + ets -> ets:info(Table, size); + mnesia -> mnesia:table_info(Table, size) + end, + case Size > 0 of + false -> + Parent ! {self(), '$end_of_table'}, + normal; + true when Type =:= ets -> + Mem = ets:info(Table, memory), + Average = Mem div Size, + NoElements = max(10, 20000 div Average), + get_ets_loop(Parent, ets:match(Table, '$1', NoElements)); + true -> + Mem = mnesia:table_info(Table, memory), + Average = Mem div Size, + NoElements = max(10, 20000 div Average), + Ms = [{'$1', [], ['$1']}], + Get = fun() -> + get_mnesia_loop(Parent, mnesia:select(Table, Ms, NoElements, read)) + end, + %% Not a transaction, we don't want to grab locks when inspecting the table + mnesia:async_dirty(Get) + end. - +get_ets_loop(Parent, '$end_of_table') -> + Parent ! {self(), '$end_of_table'}; +get_ets_loop(Parent, {Match, Cont}) -> + Parent ! {self(), Match}, + get_ets_loop(Parent, ets:match(Cont)). + +get_mnesia_loop(Parent, '$end_of_table') -> + Parent ! {self(), '$end_of_table'}; +get_mnesia_loop(Parent, {Match, Cont}) -> + Parent ! {self(), Match}, + get_mnesia_loop(Parent, mnesia:select(Cont)). + +get_table_list(ets, Opts) -> + HideUnread = proplists:get_value(unread_hidden, Opts, true), + HideSys = proplists:get_value(sys_hidden, Opts, true), + Info = fun(Id, Acc) -> + try + TabId = case ets:info(Id, named_table) of + true -> ignore; + false -> Id + end, + Name = ets:info(Id, name), + Protection = ets:info(Id, protection), + ignore(HideUnread andalso Protection == private, unreadable), + Owner = ets:info(Id, owner), + RegName = case catch process_info(Owner, registered_name) of + [] -> ignore; + {registered_name, ProcName} -> ProcName + end, + ignore(HideSys andalso ordsets:is_element(RegName, sys_processes()), system_tab), + ignore(HideSys andalso ordsets:is_element(Name, sys_tables()), system_tab), + ignore((RegName == mnesia_monitor) + andalso Name /= schema + andalso is_atom((catch mnesia:table_info(Name, where_to_read))), mnesia_tab), + Memory = ets:info(Id, memory) * erlang:system_info(wordsize), + Tab = [{name,Name}, + {id,TabId}, + {protection,Protection}, + {owner,Owner}, + {size,ets:info(Id, size)}, + {reg_name,RegName}, + {type,ets:info(Id, type)}, + {keypos,ets:info(Id, keypos)}, + {heir,ets:info(Id, heir)}, + {memory,Memory}, + {compressed,ets:info(Id, compressed)}, + {fixed,ets:info(Id, fixed)} + ], + [Tab|Acc] + catch _:_What -> + %% io:format("Skipped ~p: ~p ~n",[Id, _What]), + Acc + end + end, + lists:foldl(Info, [], ets:all()); + +get_table_list(mnesia, Opts) -> + HideSys = proplists:get_value(sys_hidden, Opts, true), + Owner = ets:info(schema, owner), + Owner /= undefined orelse + throw({error, "Mnesia is not running on: " ++ atom_to_list(node())}), + {registered_name, RegName} = process_info(Owner, registered_name), + Info = fun(Id, Acc) -> + try + Name = Id, + ignore(HideSys andalso ordsets:is_element(Name, mnesia_tables()), system_tab), + ignore(Name =:= schema, mnesia_tab), + Storage = mnesia:table_info(Id, storage_type), + Tab0 = [{name,Name}, + {owner,Owner}, + {size,mnesia:table_info(Id, size)}, + {reg_name,RegName}, + {type,mnesia:table_info(Id, type)}, + {keypos,2}, + {memory,mnesia:table_info(Id, memory) * erlang:system_info(wordsize)}, + {storage,Storage}, + {index,mnesia:table_info(Id, index)} + ], + Tab = if Storage == disc_only_copies -> + [{fixed, dets:info(Id, safe_fixed)}|Tab0]; + (Storage == ram_copies) orelse + (Storage == disc_copies) -> + [{fixed, ets:info(Id, fixed)}, + {compressed, ets:info(Id, compressed)}|Tab0]; + true -> Tab0 + end, + [Tab|Acc] + catch _:_What -> + %% io:format("Skipped ~p: ~p ~p ~n",[Id, _What, erlang:get_stacktrace()]), + Acc + end + end, + lists:foldl(Info, [], mnesia:system_info(tables)). + +fetch_stats(Parent, Time) -> + erlang:system_flag(scheduler_wall_time, true), + process_flag(trap_exit, true), + fetch_stats_loop(Parent, Time), + erlang:system_flag(scheduler_wall_time, false). + +fetch_stats_loop(Parent, Time) -> + receive + _Msg -> normal + after Time -> + _M = Parent ! {stats, 1, + erlang:statistics(scheduler_wall_time), + erlang:statistics(io), + erlang:memory()}, + fetch_stats(Parent, Time) + end. %% %% etop backend %% @@ -92,16 +263,22 @@ etop_collect([], Acc) -> Acc. %% %% ttb backend %% -ttb_init_node(MetaFile,PI,Traci) -> +ttb_init_node(MetaFile_0,PI,Traci) -> if - is_list(MetaFile); - is_atom(MetaFile) -> + is_list(MetaFile_0); + is_atom(MetaFile_0) -> + {ok, Cwd} = file:get_cwd(), + MetaFile = filename:join(Cwd, MetaFile_0), file:delete(MetaFile); true -> % {local,_,_} - ok + MetaFile = MetaFile_0 + end, + case proplists:get_value(resume, Traci) of + {true, _} -> (autostart_module()):write_config(Traci); + _ -> ok end, Self = self(), - MetaPid = spawn(fun() -> ttb_meta_tracer(MetaFile,PI,Self) end), + MetaPid = spawn(fun() -> ttb_meta_tracer(MetaFile,PI,Self,Traci) end), receive {MetaPid,started} -> ok end, MetaPid ! {metadata,Traci}, case PI of @@ -111,13 +288,14 @@ ttb_init_node(MetaFile,PI,Traci) -> false -> ok end, - {ok,MetaPid}. + {ok,MetaFile,MetaPid}. ttb_write_trace_info(MetaPid,Key,What) -> MetaPid ! {metadata,Key,What}, ok. -ttb_meta_tracer(MetaFile,PI,Parent) -> +ttb_meta_tracer(MetaFile,PI,Parent,SessionData) -> + erlang:monitor(process, proplists:get_value(ttb_control, SessionData)), case PI of true -> ReturnMS = [{'_',[],[{return_trace}]}], @@ -130,22 +308,29 @@ ttb_meta_tracer(MetaFile,PI,Parent) -> ok end, Parent ! {self(),started}, - ttb_meta_tracer_loop(MetaFile,PI,dict:new()). + case proplists:get_value(overload_check, SessionData) of + {Ms, M, F} -> + catch M:F(init), + erlang:send_after(Ms, self(), overload_check); + _ -> + ok + end, + ttb_meta_tracer_loop(MetaFile,PI,dict:new(),SessionData). -ttb_meta_tracer_loop(MetaFile,PI,Acc) -> +ttb_meta_tracer_loop(MetaFile,PI,Acc,State) -> receive {trace_ts,_,call,{erlang,register,[Name,Pid]},_} -> ttb_store_meta({pid,{Pid,Name}},MetaFile), - ttb_meta_tracer_loop(MetaFile,PI,Acc); + ttb_meta_tracer_loop(MetaFile,PI,Acc,State); {trace_ts,_,call,{global,register_name,[Name,Pid]},_} -> ttb_store_meta({pid,{Pid,{global,Name}}},MetaFile), - ttb_meta_tracer_loop(MetaFile,PI,Acc); + ttb_meta_tracer_loop(MetaFile,PI,Acc,State); {trace_ts,CallingPid,call,{erlang,spawn_opt,[{M,F,Args,_}]},_} -> MFA = {M,F,length(Args)}, NewAcc = dict:update(CallingPid, fun(Old) -> [MFA|Old] end, [MFA], Acc), - ttb_meta_tracer_loop(MetaFile,PI,NewAcc); + ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State); {trace_ts,CallingPid,return_from,{erlang,spawn_opt,_Arity},Ret,_} -> case Ret of {NewPid,_Mref} when is_pid(NewPid) -> ok; @@ -158,14 +343,14 @@ ttb_meta_tracer_loop(MetaFile,PI,Acc) -> T end, Acc), - ttb_meta_tracer_loop(MetaFile,PI,NewAcc); + ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State); {trace_ts,CallingPid,call,{erlang,Spawn,[M,F,Args]},_} when Spawn==spawn;Spawn==spawn_link -> MFA = {M,F,length(Args)}, NewAcc = dict:update(CallingPid, fun(Old) -> [MFA|Old] end, [MFA], Acc), - ttb_meta_tracer_loop(MetaFile,PI,NewAcc); + ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State); {trace_ts,CallingPid,return_from,{erlang,Spawn,_Arity},NewPid,_} when Spawn==spawn;Spawn==spawn_link -> @@ -176,28 +361,53 @@ ttb_meta_tracer_loop(MetaFile,PI,Acc) -> T end, Acc), - ttb_meta_tracer_loop(MetaFile,PI,NewAcc); + ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State); {metadata,Data} when is_list(Data) -> ttb_store_meta(Data,MetaFile), - ttb_meta_tracer_loop(MetaFile,PI,Acc); + ttb_meta_tracer_loop(MetaFile,PI,Acc,State); {metadata,Key,Fun} when is_function(Fun) -> ttb_store_meta([{Key,Fun()}],MetaFile), - ttb_meta_tracer_loop(MetaFile,PI,Acc); + ttb_meta_tracer_loop(MetaFile,PI,Acc,State); {metadata,Key,What} -> ttb_store_meta([{Key,What}],MetaFile), - ttb_meta_tracer_loop(MetaFile,PI,Acc); - - stop when PI=:=true -> - erlang:trace_pattern({erlang,spawn,3},false,[meta]), + ttb_meta_tracer_loop(MetaFile,PI,Acc,State); + overload_check -> + {Ms, M, F} = proplists:get_value(overload_check, State), + case catch M:F(check) of + true -> + erlang:trace(all, false, [all]), + ControlPid = proplists:get_value(ttb_control, State), + ControlPid ! {node_overloaded, node()}, + catch M:F(stop), + ttb_meta_tracer_loop(MetaFile,PI,Acc,lists:keydelete(overload_check, 1, State)); + _ -> + erlang:send_after(Ms, self(), overload_check), + ttb_meta_tracer_loop(MetaFile,PI,Acc, State) + end; + {'DOWN', _, _, _, _} -> + stop_seq_trace(), + self() ! stop, + ttb_meta_tracer_loop(MetaFile,PI,Acc, State); + stop when PI=:=true -> + try_stop_resume(State), + try_stop_overload_check(State), + erlang:trace_pattern({erlang,spawn,3},false,[meta]), erlang:trace_pattern({erlang,spawn_link,3},false,[meta]), erlang:trace_pattern({erlang,spawn_opt,1},false,[meta]), erlang:trace_pattern({erlang,register,2},false,[meta]), erlang:trace_pattern({global,register_name,2},false,[meta]); stop -> - ok + try_stop_resume(State), + try_stop_overload_check(State) + end. + +try_stop_overload_check(State) -> + case proplists:get_value(overload, State) of + undefined -> ok; + {_, M, F} -> catch M:F(stop) end. pnames() -> @@ -222,6 +432,40 @@ pinfo(P,Globals) -> undefined -> [] % the process has terminated end. +autostart_module() -> + element(2, application:get_env(runtime_tools, ttb_autostart_module)). + +try_stop_resume(State) -> + case proplists:get_value(resume, State) of + true -> (autostart_module()):delete_config(); + _ -> ok + end. + +ttb_resume_trace() -> + case (autostart_module()):read_config() of + {error, _} -> + ok; + {ok, Data} -> + Pid = proplists:get_value(ttb_control, Data), + {_, Timeout} = proplists:get_value(resume, Data), + case rpc:call(node(Pid), erlang, whereis, [ttb]) of + Pid -> + Pid ! {noderesumed, node(), self()}, + wait_for_fetch_ready(Timeout); + _ -> + ok + end, + (autostart_module()):delete_config(), + ok + end. + +wait_for_fetch_ready(Timeout) -> + receive + trace_resumed -> + ok + after Timeout -> + ok + end. ttb_store_meta(Data,{local,MetaFile,Port}) when is_list(Data) -> ttb_send_to_port(Port,MetaFile,Data); @@ -273,6 +517,9 @@ ttb_stop(MetaPid) -> %% returns, and then the Port (in {local,MetaFile,Port}) %% cannot be accessed any more. receive {'DOWN', Ref, process, MetaPid, _Info} -> ok end, + stop_seq_trace(). + +stop_seq_trace() -> seq_trace:reset_trace(), seq_trace:set_system_tracer(false). @@ -287,7 +534,7 @@ ttb_fetch(MetaFile,{Port,Host}) -> send_files({Sock,Host},[File|Files]) -> {ok,Fd} = file:open(File,[raw,read,binary]), - gen_tcp:send(Sock,<<1,(list_to_binary(File))/binary>>), + gen_tcp:send(Sock,<<1,(list_to_binary(filename:basename(File)))/binary>>), send_chunks(Sock,Fd), file:delete(File), send_files({Sock,Host},Files); @@ -318,3 +565,62 @@ match_filenames(Dir,MetaFile,[H|T],Files) -> end; match_filenames(_Dir,_MetaFile,[],Files) -> Files. + + +%%%%%%%%%%%%%%%%% + +sys_tables() -> + [ac_tab, asn1, + cdv_dump_index_table, cdv_menu_table, cdv_decode_heap_table, + cell_id, cell_pos, clist, + cover_internal_data_table, cover_collected_remote_data_table, cover_binary_code_table, + code, code_names, cookies, + corba_policy, corba_policy_associations, + dets, dets_owners, dets_registry, + disk_log_names, disk_log_pids, + eprof, erl_atom_cache, erl_epmd_nodes, + etop_accum_tab, etop_tr, + ets_coverage_data, + file_io_servers, + gs_mapping, gs_names, gstk_db, + gstk_grid_cellid, gstk_grid_cellpos, gstk_grid_id, + httpd, + id, + ign_req_index, ign_requests, + index, + inet_cache, inet_db, inet_hosts, + 'InitialReferences', + int_db, + interpreter_includedirs_macros, + ir_WstringDef, + lmcounter, locks, + % mnesia_decision, + mnesia_gvar, mnesia_stats, + % mnesia_transient_decision, + pg2_table, + queue, + schema, + shell_records, + snmp_agent_table, snmp_local_db2, snmp_mib_data, snmp_note_store, snmp_symbolic_ets, + tkFun, tkLink, tkPriv, + ttb, ttb_history_table, + udp_fds, udp_pids + ]. + +sys_processes() -> + [auth, code_server, global_name_server, inet_db, + mnesia_recover, net_kernel, timer_server, wxe_master]. + +mnesia_tables() -> + [ir_AliasDef, ir_ArrayDef, ir_AttributeDef, ir_ConstantDef, + ir_Contained, ir_Container, ir_EnumDef, ir_ExceptionDef, + ir_IDLType, ir_IRObject, ir_InterfaceDef, ir_ModuleDef, + ir_ORB, ir_OperationDef, ir_PrimitiveDef, ir_Repository, + ir_SequenceDef, ir_StringDef, ir_StructDef, ir_TypedefDef, + ir_UnionDef, logTable, logTransferTable, mesh_meas, + mesh_type, mnesia_clist, orber_CosNaming, + orber_objkeys, user + ]. + +ignore(true, Reason) -> throw(Reason); +ignore(_,_ ) -> ok. diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src index e6dc7a21d4..1152f7259d 100644 --- a/lib/runtime_tools/src/runtime_tools.app.src +++ b/lib/runtime_tools/src/runtime_tools.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,7 +22,8 @@ {modules, [dbg,observer_backend,percept_profile, inviso_rt,inviso_rt_lib,inviso_rt_meta, inviso_as_lib,inviso_autostart,inviso_autostart_server, - runtime_tools,runtime_tools_sup,erts_alloc_config]}, + runtime_tools,runtime_tools_sup,erts_alloc_config, + ttb_autostart,dyntrace]}, {registered, [runtime_tools_sup,inviso_rt,inviso_rt_meta]}, {applications, [kernel, stdlib]}, % {env, [{inviso_autostart_mod,your_own_autostart_module}]}, diff --git a/lib/runtime_tools/src/runtime_tools_sup.erl b/lib/runtime_tools/src/runtime_tools_sup.erl index 1a872c355d..913719c449 100644 --- a/lib/runtime_tools/src/runtime_tools_sup.erl +++ b/lib/runtime_tools/src/runtime_tools_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,6 +38,8 @@ init(AutoModArgs) -> Flags = {one_for_one, 0, 3600}, Children = [{inviso_rt, {inviso_rt, start_link_auto, [AutoModArgs]}, - temporary, 3000, worker, [inviso_rt]}], + temporary, 3000, worker, [inviso_rt]}, + {ttb_autostart, {ttb_autostart, start_link, []}, + temporary, 3000, worker, [ttb_autostart]}], {ok, {Flags, Children}}. %% ----------------------------------------------------------------------------- diff --git a/lib/runtime_tools/src/ttb_autostart.erl b/lib/runtime_tools/src/ttb_autostart.erl new file mode 100644 index 0000000000..4c6971c119 --- /dev/null +++ b/lib/runtime_tools/src/ttb_autostart.erl @@ -0,0 +1,55 @@ +%%%------------------------------------------------------------------- +%%% File : ttb_autostart.erl +%%% Author : Bartłomiej Puzoń <[email protected]> +%%% Description : This supervisor is used to resume ttb tracing +%%% Users are able to provide custom restart modules for *_config, as +%%% file:write/read/delete may not be possible on diskless nodes. +%%% +%%% Created : 31 Jul 2010 by <[email protected]> +%%%------------------------------------------------------------------- +-module(ttb_autostart). + +-behaviour(gen_server). + +%% API +-export([start_link/0, + read_config/0, + write_config/1, + delete_config/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-define(DEF_AUTOSTART_MODULE, ?MODULE). +-define(AUTOSTART_FILENAME, "ttb_autostart.bin"). + +start_link() -> + gen_server:start_link(?MODULE, no_args, []). + +delete_config() -> + file:delete(?AUTOSTART_FILENAME). + +read_config() -> + case file:read_file(?AUTOSTART_FILENAME) of + {ok, Data} -> {ok, binary_to_term(Data)}; + Error -> Error + end. + +write_config(Data) -> + file:write_file(?AUTOSTART_FILENAME, term_to_binary(Data)). + +init(no_args) -> + case application:get_env(runtime_tools, ttb_autostart_module) of + {ok, _} -> ok; + undefined -> application:set_env(runtime_tools, ttb_autostart_module, ?DEF_AUTOSTART_MODULE) + end, + observer_backend:ttb_resume_trace(), + %%As the process is not needed any more, it will shut itself down + {ok, no_args, 10000}. + +handle_call(_,_,_) -> {noreply, no_args}. +handle_cast(_,_) -> {noreply, no_args}. +handle_info(timeout,_) -> {stop, normal, no_args}. +terminate(_,_) -> ok. +code_change(_,_,_) -> {ok, no_args}. diff --git a/lib/runtime_tools/test/Makefile b/lib/runtime_tools/test/Makefile index cfaf420d65..3e50ba02ca 100644 --- a/lib/runtime_tools/test/Makefile +++ b/lib/runtime_tools/test/Makefile @@ -3,6 +3,7 @@ include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES = \ + dyntrace_SUITE \ runtime_tools_SUITE \ inviso_testmodule1_foo \ inviso_SUITE \ diff --git a/lib/runtime_tools/test/dyntrace_SUITE.erl b/lib/runtime_tools/test/dyntrace_SUITE.erl new file mode 100644 index 0000000000..0e4f369ed0 --- /dev/null +++ b/lib/runtime_tools/test/dyntrace_SUITE.erl @@ -0,0 +1,224 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(dyntrace_SUITE). +-include_lib("test_server/include/test_server.hrl"). + +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2]). + +%% Test cases +-export([smoke/1,process/1]). + +%% Default timetrap timeout (set in init_per_testcase) +-define(default_timeout, ?t:minutes(1)). + +init_per_testcase(_Case, Config) -> + Dog = test_server:timetrap(?default_timeout), + [{watchdog,Dog}|Config]. + +end_per_testcase(_Case, Config) -> + Dog = ?config(watchdog, Config), + ?t:timetrap_cancel(Dog), + ok. + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + case erlang:system_info(dynamic_trace) of + none -> + {skip,"No dynamic trace in this run-time system"}; + dtrace -> + [{group,smoke}]; + systemtap -> + {skip,"SystemTap tests currently not supported"} + end. + +groups() -> + [{smoke,[sequence],[smoke,{group,rest}]}, + {rest,[], + [process]}]. + +init_per_suite(Config) -> + N = "beam" ++ + case erlang:system_info(debug_compiled) of + false -> ""; + true -> ".debug" + end ++ + case erlang:system_info(smp_support) of + false -> ""; + true -> ".smp" + end, + [{emu_name,N}|Config]. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +smoke(Config) -> + Emu = ?t:lookup_config(emu_name, Config), + BinEmu = list_to_binary(Emu), + case erlang:system_info(dynamic_trace) of + dtrace -> + Probes = os:cmd("sudo /usr/sbin/dtrace -l -m" ++ Emu), + io:put_chars(Probes), + [_|Lines] = re:split(Probes, "\n", [trim]), + [{_,_} = binary:match(L, BinEmu) || L <- Lines], + ok + end, + + %% Test that the framework for running dtrace/systemtap works + %% by executing an empty script. + {ok,[]} = dyntrace("", fun() -> ok end), + ok. + + +process(_Config) -> + Script = [{probe,"process-spawn"}, + {action,[{printf,["spawn %s %s\n",{arg,0},{arg,1}]}]}, + {probe,"process-scheduled"}, + {action,[{printf,["in %s\n",{arg,0}]}]}, + {probe,"process-unscheduled"}, + {action,[{printf,["out %s\n",{arg,0}]}]}, + {probe,"process-hibernate"}, + {action,[{printf,["hibernate %s %s\n",{arg,0},{arg,1}]}]}, + {probe,"process-exit"}, + {action,[{printf,["exit %s %s\n",{arg,0},{arg,1}]}]} + ], + F = fun() -> + {Pid,Ref} = spawn_monitor(fun my_process/0), + Pid ! hibernate, + Pid ! quit, + receive + {'DOWN',Ref,process,Pid,{terminated,Pid}} -> + ok + end, + Pid + end, + {Pid,Output0} = dyntrace(Script, F), + Output1 = [termify_line(L) || L <- Output0], + PidStr = pid_to_list(Pid), + Output = [L || L <- Output1, element(2, L) =:= PidStr], + Reason = "{terminated,"++PidStr++"}", + io:format("~p\n", [Output]), + [{spawn,PidStr,"erlang:apply/2"}, + {in,PidStr}, + {hibernate,PidStr,"erlang:apply/2"}, + {out,PidStr}, + {in,PidStr}, + {exit,PidStr,Reason}, + {out,PidStr}] = Output, + ok. + +termify_line(L) -> + [H|T] = re:split(L, " ", [{return,list}]), + list_to_tuple([list_to_atom(H)|T]). + +my_process() -> + receive + hibernate -> + erlang:hibernate(erlang, apply, [fun my_process/0,[]]); + quit -> + exit({terminated,self()}) + end. + +%%% +%%% Utility functions. +%%% + +dyntrace(Script0, Action) -> + Sudo = os:find_executable(sudo), + {Termination,Pid} = termination_probe(), + Script1 = Script0++Termination, + Script = translate_script(Script1), + io:format("~s\n", [Script]), + SrcFile = "test-dyntrace.d", + ok = file:write_file(SrcFile, Script), + Args = ["/usr/sbin/dtrace", "-q","-s",SrcFile], + Port = open_port({spawn_executable,Sudo}, + [{args,Args},stream,in,stderr_to_stdout,eof]), + receive + {Port,{data,Sofar}} -> + Res = Action(), + Pid ! quit, + {Res,get_data(Port, Sofar)} + end. + +get_data(Port, Sofar) -> + receive + {Port,{data,Bytes}} -> + get_data(Port, [Sofar|Bytes]); + {Port,eof} -> + port_close(Port), + [$\n|T] = lists:flatten(Sofar), + re:split(T, "\n", [{return,list},trim]) + end. + +termination_probe() -> + Pid = spawn(fun() -> + receive + _ -> + exit(done) + end + end), + S = [{'BEGIN',[{printf,["\n"]}]}, + {probe,"process-exit"}, + {pred,{'==',{arg,0},Pid}}, + {action,[{exit,[0]}]}], + {S,Pid}. + +translate_script(Script) -> + [dtrace_op(Op) || Op <- Script]. + +dtrace_op({probe,Function}) -> + OsPid = os:getpid(), + ["erlang",OsPid,":::",Function,$\n]; +dtrace_op({pred,Pred}) -> + ["/",dtrace_op(Pred),"/\n"]; +dtrace_op({action,List}) -> + ["{ ",action_list(List)," }\n\n"]; +dtrace_op({'BEGIN',List}) -> + ["BEGIN { ",action_list(List)," }\n\n"]; +dtrace_op({'==',Op1,Op2}) -> + [dtrace_op(Op1)," == ",dtrace_op(Op2)]; +dtrace_op({arg,N}) -> + ["copyinstr(arg",integer_to_list(N),")"]; +dtrace_op({Func,List}) when is_atom(Func), is_list(List) -> + [atom_to_list(Func),"(",comma_sep_ops(List),")"]; +dtrace_op(Pid) when is_pid(Pid) -> + ["\"",pid_to_list(Pid),"\""]; +dtrace_op(Str) when is_integer(hd(Str)) -> + io_lib:format("~p", [Str]); +dtrace_op(Int) when is_integer(Int) -> + integer_to_list(Int). + +comma_sep_ops([A,B|T]) -> + [dtrace_op(A),","|comma_sep_ops([B|T])]; +comma_sep_ops([H]) -> + dtrace_op(H); +comma_sep_ops([]) -> []. + +action_list([H|T]) -> + [dtrace_op(H),";"|action_list(T)]; +action_list([]) -> []. diff --git a/lib/runtime_tools/test/inviso_SUITE.erl b/lib/runtime_tools/test/inviso_SUITE.erl index 3ae8d34dd6..c64c40b945 100644 --- a/lib/runtime_tools/test/inviso_SUITE.erl +++ b/lib/runtime_tools/test/inviso_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -66,13 +66,18 @@ end_per_group(_GroupName, Config) -> init_per_suite(Config) -> - %% No never know who skrewed up this node before this suite! :-) - erlang:trace_pattern({'_','_','_'},[],[local]), - erlang:trace_pattern({'_','_','_'},[],[global]), - erlang:trace(all,false,[all]), + case test_server:is_native(lists) of + true -> + {skip,"Native libs -- tracing doesn't work"}; + false -> + %% We never know who messed up this node before this suite! :-) + erlang:trace_pattern({'_','_','_'},[],[local]), + erlang:trace_pattern({'_','_','_'},[],[global]), + erlang:trace(all,false,[all]), - ?l ok=application:start(runtime_tools), - Config. + ok=application:start(runtime_tools), + Config + end. end_per_suite(_Config) -> ?l ok=application:stop(runtime_tools). @@ -1380,9 +1385,10 @@ fetch_log_dist_trace_2(Config) -> io:format("~p~n",[NodeResults]), CheckFun=fun({N,{complete,[{trace_log,FileResults1},{ti_log,[{ok,TiFile}]}]}}) -> Fun2=fun({ok,File}) -> - {match,1,_}= - regexp:first_match(File, - "^"++"p1"++Name++atom_to_list(N)), + match= + re:run(File, + "^"++"p1"++Name++atom_to_list(N), + [{capture,none}]), true; (_) -> false @@ -1425,8 +1431,8 @@ fetch_log_dist_trace_3(Config) -> CheckFun=fun({N,{ok,[{trace_log,PrivDir2,[F1,F2]},{ti_log,PrivDir2,[F3]}]}})-> PrivDir2=PrivDir, RegExp="^"++Name++atom_to_list(N)++"[0-9]+"++"\.log", - {match,1,_}=regexp:first_match(F1,RegExp), - {match,1,_}=regexp:first_match(F2,RegExp), + match=re:run(F1,RegExp,[{capture,none}]), + match=re:run(F2,RegExp,[{capture,none}]), F3=Name++"_ti_"++atom_to_list(N)++".ti", true; (_) -> @@ -1439,9 +1445,10 @@ fetch_log_dist_trace_3(Config) -> io:format("~p~n",[NodeResults2]), CheckFun2=fun({N,{complete,[{trace_log,FileResults1},{ti_log,[{ok,TiFile}]}]}}) -> Fun2=fun({ok,File}) -> - {match,1,_}= - regexp:first_match(File, - "^"++"p1"++Name++atom_to_list(N)), + match= + re:run(File, + "^"++"p1"++Name++atom_to_list(N), + [{capture,none}]), true; (_) -> false @@ -2649,8 +2656,8 @@ check_on_nodes([],_,_,_,_) -> how_many_files_regexp([],_,N) -> {ok,N}; how_many_files_regexp([FName|Rest],RegExp,N) -> - case regexp:first_match(FName,RegExp) of - {match,1,_} -> + case re:run(FName,RegExp,[{capture,none}]) of + match -> how_many_files_regexp(Rest,RegExp,N+1); nomatch -> how_many_files_regexp(Rest,RegExp,N); diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk index 0bcd261861..c9b5cc1a41 100644 --- a/lib/runtime_tools/vsn.mk +++ b/lib/runtime_tools/vsn.mk @@ -1 +1 @@ -RUNTIME_TOOLS_VSN = 1.8.6 +RUNTIME_TOOLS_VSN = 1.8.8 |