diff options
author | Patrik Nyblom <[email protected]> | 2012-03-22 18:29:34 +0100 |
---|---|---|
committer | Patrik Nyblom <[email protected]> | 2012-03-22 18:30:07 +0100 |
commit | 5957a8338fe1f4e79a39277174094bbd9e978896 (patch) | |
tree | 4abcf47850e2a577b401a61bb1693d85cb137ffe /erts/emulator | |
parent | b5f6596f49db47e6dacddb2b10292a7294e13993 (diff) | |
parent | 1e653c3539423e4cfdeab1d232483bcac0e8b073 (diff) | |
download | otp-5957a8338fe1f4e79a39277174094bbd9e978896.tar.gz otp-5957a8338fe1f4e79a39277174094bbd9e978896.tar.bz2 otp-5957a8338fe1f4e79a39277174094bbd9e978896.zip |
Merge branch 'pan/dtrace' into maint
* pan/dtrace: (22 commits)
Use distinct function-entry probes for local and global calls
Correct calculation of stack depth in call/return probes
Add probes for all kind of calls
Don't try to "clean up" generated fun names
erl_process.c: Fix probe for process exit
Remove code causing dialyzer warning from prim_file
Add documentation for dyntrace and system_info changes
Update slogan and add system_info for dynamic trace
Update README's for dtrace and systemtap
Rename dyntrace BIFs to more suiting names
If VM probes are not enabled, short-circuit calls to probe BIFs
beam_makeops: Add a simple preprocessor
Ifdef all dynamic trace code
Move dtrace erlang code and NIF into runtime_tools
Correct some errors in the user tag spreading
Change to more specific configure options for dtrace
Add user tag spreading functionality to VM and use in file
Update dtrace for changes in R15
Add DTrace support for OS X, Solaris, and Linux (via SystemTap), 4/4
Add DTrace support for OS X, Solaris, and Linux (via SystemTap), 3/4
...
OTP-10017
Diffstat (limited to 'erts/emulator')
36 files changed, 3288 insertions, 180 deletions
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 279844adb2..2efbe2d57e 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -23,6 +23,9 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk ENABLE_ALLOC_TYPE_VARS = @ENABLE_ALLOC_TYPE_VARS@ HIPE_ENABLED=@HIPE_ENABLED@ +DTRACE_ENABLED=@DTRACE_ENABLED@ +DTRACE_ENABLED_2STEP=@DTRACE_ENABLED_2STEP@ +USE_VM_PROBES=@USE_VM_PROBES@ LIBS = @LIBS@ Z_LIB=@Z_LIB@ NO_INLINE_FUNCTIONS=false @@ -483,6 +486,14 @@ GENERATE += $(HIPE_ASM) \ endif endif +ifdef DTRACE_ENABLED +# global.h causes problems by including dtrace-wrapper.h which includes +# the autogenerated erlang_dtrace.h ... so make erlang_dtrace.h very early. +generate: $(TARGET)/erlang_dtrace.h $(GENERATE) +else +generate: $(GENERATE) +endif + ifdef HIPE_ENABLED OPCODE_TABLES += hipe/hipe_ops.tab endif @@ -498,6 +509,7 @@ $(TTF_DIR)/OPCODES-GENERATED: $(OPCODE_TABLES) utils/beam_makeops LANG=C $(PERL) utils/beam_makeops \ -wordsize @EXTERNAL_WORD_SIZE@ \ -outdir $(TTF_DIR) \ + -DUSE_VM_PROBES=$(if $(USE_VM_PROBES),1,0) \ -emulator $(OPCODE_TABLES) && echo $? >$(TTF_DIR)/OPCODES-GENERATED GENERATE += $(TTF_DIR)/OPCODES-GENERATED @@ -590,6 +602,11 @@ $(TTF_DIR)/GENERATED: $(GENERATE) echo $? >$(TTF_DIR)/GENERATED endif +$(TARGET)/erlang_dtrace.h: beam/erlang_dtrace.d + dtrace -h -C -Ibeam -s $< -o ./erlang_dtrace.tmp + sed -e '/^#define[ ]*ERLANG_[A-Z0-9_]*(.*)/y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' ./erlang_dtrace.tmp > $@ + rm ./erlang_dtrace.tmp + # ---------------------------------------------------------------------- # Pattern rules # @@ -633,7 +650,6 @@ $(OBJDIR)/beam_emu.o: beam/beam_emu.c $(EMU_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@ endif - $(OBJDIR)/%.o: beam/%.c $(CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@ @@ -833,7 +849,18 @@ endif BASE_OBJS = $(RUN_OBJS) $(EMU_OBJS) $(OS_OBJS) $(EXTRA_BASE_OBJS) -OBJS = $(BASE_OBJS) $(DRV_OBJS) +before_DTrace_OBJS = $(BASE_OBJS) $(DRV_OBJS) + +DTRACE_OBJS = +ifdef DTRACE_ENABLED_2STEP +DTRACE_OBJS = $(OBJDIR)/erlang_dtrace.o +$(OBJDIR)/erlang_dtrace.o: $(before_DTrace_OBJS) $(TARGET)/erlang_dtrace.h + dtrace -G -C -Ibeam \ + -s beam/erlang_dtrace.d \ + -o $@ $(before_DTrace_OBJS) +endif + +OBJS = $(before_DTrace_OBJS) $(DTRACE_OBJS) $(INIT_OBJS): $(TTF_DIR)/GENERATED $(OBJS): $(TTF_DIR)/GENERATED diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 7be40976f6..9ce1068b23 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -248,6 +248,7 @@ atom global_heaps_size atom Gt='>' atom grun atom group_leader +atom have_dt_utag atom heap_block_size atom heap_size atom heap_sizes diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index c65b2be106..8b4f067b98 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -41,6 +41,7 @@ #include "hipe_mode_switch.h" #include "hipe_bif1.h" #endif +#include "dtrace-wrapper.h" /* #define HARDDEBUG 1 */ @@ -1050,6 +1051,101 @@ init_emulator(void) # define REG_tmp_arg2 #endif +#ifdef USE_VM_PROBES +# define USE_VM_CALL_PROBES +#endif + +#ifdef USE_VM_CALL_PROBES + +#define DTRACE_LOCAL_CALL(p, m, f, a) \ + if (DTRACE_ENABLED(local_function_entry)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + int depth = STACK_START(p) - STACK_TOP(p); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE3(local_function_entry, process_name, mfa, depth); \ + } + +#define DTRACE_GLOBAL_CALL(p, m, f, a) \ + if (DTRACE_ENABLED(global_function_entry)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + int depth = STACK_START(p) - STACK_TOP(p); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE3(global_function_entry, process_name, mfa, depth); \ + } + +#define DTRACE_RETURN(p, m, f, a) \ + if (DTRACE_ENABLED(function_return)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + int depth = STACK_START(p) - STACK_TOP(p); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE3(function_return, process_name, mfa, depth); \ + } + +#define DTRACE_BIF_ENTRY(p, m, f, a) \ + if (DTRACE_ENABLED(bif_entry)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE2(bif_entry, process_name, mfa); \ + } + +#define DTRACE_BIF_RETURN(p, m, f, a) \ + if (DTRACE_ENABLED(bif_return)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE2(bif_return, process_name, mfa); \ + } + +#define DTRACE_NIF_ENTRY(p, m, f, a) \ + if (DTRACE_ENABLED(nif_entry)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE2(nif_entry, process_name, mfa); \ + } + +#define DTRACE_NIF_RETURN(p, m, f, a) \ + if (DTRACE_ENABLED(nif_return)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE2(nif_return, process_name, mfa); \ + } + +#else /* USE_VM_PROBES */ + +#define DTRACE_LOCAL_CALL(p, m, f, a) do {} while (0) +#define DTRACE_GLOBAL_CALL(p, m, f, a) do {} while (0) +#define DTRACE_RETURN(p, m, f, a) do {} while (0) +#define DTRACE_BIF_ENTRY(p, m, f, a) do {} while (0) +#define DTRACE_BIF_RETURN(p, m, f, a) do {} while (0) +#define DTRACE_NIF_ENTRY(p, m, f, a) do {} while (0) +#define DTRACE_NIF_RETURN(p, m, f, a) do {} while (0) + +#endif /* USE_VM_PROBES */ + +#ifdef USE_VM_PROBES +void +dtrace_drvport_str(ErlDrvPort drvport, char *port_buf) +{ + Port *port = erts_drvport2port(drvport); + + erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>", + port_channel_no(port->id), + port_number(port->id)); +} +#endif /* * process_main() is called twice: * The first call performs some initialisation, including exporting @@ -1221,6 +1317,30 @@ void process_main(void) #endif SWAPIN; ASSERT(VALID_INSTR(next)); + +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_scheduled)) { + DTRACE_CHARBUF(process_buf, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(fun_buf, DTRACE_TERM_BUF_SIZE); + dtrace_proc_str(c_p, process_buf); + + if (ERTS_PROC_IS_EXITING(c_p)) { + strcpy(fun_buf, "<exiting>"); + } else { + BeamInstr *fptr = find_function_from_pc(c_p->i); + if (fptr) { + dtrace_fun_decode(c_p, (Eterm)fptr[0], + (Eterm)fptr[1], (Uint)fptr[2], + NULL, fun_buf); + } else { + erts_snprintf(fun_buf, sizeof(fun_buf), + "<unknown/%p>", next); + } + } + + DTRACE2(process_scheduled, process_buf, fun_buf); + } +#endif Goto(next); } @@ -1397,6 +1517,7 @@ void process_main(void) /* FALL THROUGH */ OpCase(i_call_only_f): { SET_I((BeamInstr *) Arg(0)); + DTRACE_LOCAL_CALL(c_p, (Eterm)I[-3], (Eterm)I[-2], I[-1]); Dispatch(); } @@ -1408,6 +1529,7 @@ void process_main(void) RESTORE_CP(E); E = ADD_BYTE_OFFSET(E, Arg(1)); SET_I((BeamInstr *) Arg(0)); + DTRACE_LOCAL_CALL(c_p, (Eterm)I[-3], (Eterm)I[-2], I[-1]); Dispatch(); } @@ -1419,6 +1541,7 @@ void process_main(void) OpCase(i_call_f): { SET_CP(c_p, I+2); SET_I((BeamInstr *) Arg(0)); + DTRACE_LOCAL_CALL(c_p, (Eterm)I[-3], (Eterm)I[-2], I[-1]); Dispatch(); } @@ -1435,6 +1558,12 @@ void process_main(void) * is not loaded, it points to code which will invoke the error handler * (see lb_call_error_handler below). */ +#ifdef USE_VM_CALL_PROBES + if (DTRACE_ENABLED(global_function_entry)) { + BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address); + DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]); + } +#endif Dispatchx(); OpCase(i_move_call_ext_cre): { @@ -1444,6 +1573,12 @@ void process_main(void) /* FALL THROUGH */ OpCase(i_call_ext_e): SET_CP(c_p, I+2); +#ifdef USE_VM_CALL_PROBES + if (DTRACE_ENABLED(global_function_entry)) { + BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address); + DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]); + } +#endif Dispatchx(); OpCase(i_move_call_ext_only_ecr): { @@ -1451,6 +1586,12 @@ void process_main(void) } /* FALL THROUGH */ OpCase(i_call_ext_only_e): +#ifdef USE_VM_CALL_PROBES + if (DTRACE_ENABLED(global_function_entry)) { + BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address); + DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]); + } +#endif Dispatchx(); OpCase(init_y): { @@ -1486,7 +1627,16 @@ void process_main(void) OpCase(return): { +#ifdef USE_VM_CALL_PROBES + BeamInstr* fptr; +#endif SET_I(c_p->cp); + +#ifdef USE_VM_CALL_PROBES + if (DTRACE_ENABLED(function_return) && (fptr = find_function_from_pc(c_p->cp))) { + DTRACE_RETURN(c_p, (Eterm)fptr[0], (Eterm)fptr[1], (Uint)fptr[2]); + } +#endif /* * We must clear the CP to make sure that a stale value do not * create a false module dependcy preventing code upgrading. @@ -1755,6 +1905,7 @@ void process_main(void) * remove it... */ ASSERT(!msgp->data.attached); + /* TODO: Add DTrace probe for this bad message situation? */ UNLINK_MESSAGE(c_p, msgp); free_message(msgp); goto loop_rec__; @@ -1780,24 +1931,88 @@ void process_main(void) save_calls(c_p, &exp_receive); } if (ERL_MESSAGE_TOKEN(msgp) == NIL) { - SEQ_TRACE_TOKEN(c_p) = NIL; +#ifdef USE_VM_PROBES + if (DT_UTAG(c_p) != NIL) { + if (DT_UTAG_FLAGS(c_p) & DT_UTAG_PERMANENT) { + SEQ_TRACE_TOKEN(c_p) = am_have_dt_utag; +#ifdef DTRACE_TAG_HARDDEBUG + if (DT_UTAG_FLAGS(c_p) & DT_UTAG_SPREADING) + erts_fprintf(stderr, + "Dtrace -> (%T) stop spreading " + "tag %T with message %T\r\n", + c_p->id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp)); +#endif + } else { +#ifdef DTRACE_TAG_HARDDEBUG + erts_fprintf(stderr, + "Dtrace -> (%T) kill tag %T with " + "message %T\r\n", + c_p->id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp)); +#endif + DT_UTAG(c_p) = NIL; + SEQ_TRACE_TOKEN(c_p) = NIL; + } + } else { +#endif + SEQ_TRACE_TOKEN(c_p) = NIL; +#ifdef USE_VM_PROBES + } + DT_UTAG_FLAGS(c_p) &= ~DT_UTAG_SPREADING; +#endif } else if (ERL_MESSAGE_TOKEN(msgp) != am_undefined) { Eterm msg; SEQ_TRACE_TOKEN(c_p) = ERL_MESSAGE_TOKEN(msgp); - ASSERT(is_tuple(SEQ_TRACE_TOKEN(c_p))); - ASSERT(SEQ_TRACE_TOKEN_ARITY(c_p) == 5); - ASSERT(is_small(SEQ_TRACE_TOKEN_SERIAL(c_p))); - ASSERT(is_small(SEQ_TRACE_TOKEN_LASTCNT(c_p))); - ASSERT(is_small(SEQ_TRACE_TOKEN_FLAGS(c_p))); - ASSERT(is_pid(SEQ_TRACE_TOKEN_SENDER(c_p))); - c_p->seq_trace_lastcnt = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p)); - if (c_p->seq_trace_clock < unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p))) { - c_p->seq_trace_clock = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p)); +#ifdef USE_VM_PROBES + if (ERL_MESSAGE_TOKEN(msgp) == am_have_dt_utag) { + if (DT_UTAG(c_p) == NIL) { + DT_UTAG(c_p) = ERL_MESSAGE_DT_UTAG(msgp); + } + DT_UTAG_FLAGS(c_p) |= DT_UTAG_SPREADING; +#ifdef DTRACE_TAG_HARDDEBUG + erts_fprintf(stderr, + "Dtrace -> (%T) receive tag (%T) " + "with message %T\r\n", + c_p->id, DT_UTAG(c_p), ERL_MESSAGE_TERM(msgp)); +#endif + } else { +#endif + ASSERT(is_tuple(SEQ_TRACE_TOKEN(c_p))); + ASSERT(SEQ_TRACE_TOKEN_ARITY(c_p) == 5); + ASSERT(is_small(SEQ_TRACE_TOKEN_SERIAL(c_p))); + ASSERT(is_small(SEQ_TRACE_TOKEN_LASTCNT(c_p))); + ASSERT(is_small(SEQ_TRACE_TOKEN_FLAGS(c_p))); + ASSERT(is_pid(SEQ_TRACE_TOKEN_SENDER(c_p))); + c_p->seq_trace_lastcnt = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p)); + if (c_p->seq_trace_clock < unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p))) { + c_p->seq_trace_clock = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p)); + } + msg = ERL_MESSAGE_TERM(msgp); + seq_trace_output(SEQ_TRACE_TOKEN(c_p), msg, SEQ_TRACE_RECEIVE, + c_p->id, c_p); +#ifdef USE_VM_PROBES } - msg = ERL_MESSAGE_TERM(msgp); - seq_trace_output(SEQ_TRACE_TOKEN(c_p), msg, SEQ_TRACE_RECEIVE, - c_p->id, c_p); +#endif + } +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(message_receive)) { + Eterm token2 = NIL; + DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; + + dtrace_proc_str(c_p, receiver_name); + token2 = SEQ_TRACE_TOKEN(c_p); + if (token2 != NIL && token2 != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token2)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token2)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token2)); + } + DTRACE6(message_receive, + receiver_name, size_object(ERL_MESSAGE_TERM(msgp)), + c_p->msg.len - 1, tok_label, tok_lastcnt, tok_serial); } +#endif UNLINK_MESSAGE(c_p, msgp); JOIN_MESSAGE(c_p); CANCEL_TIMER(c_p); @@ -3157,6 +3372,7 @@ void process_main(void) */ BifFunction vbf; + DTRACE_NIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); c_p->current = I-3; /* current and vbf set to please handle_error */ SWAPOUT; c_p->fcalls = FCALLS - 1; @@ -3178,6 +3394,8 @@ void process_main(void) ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result)); PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); + + DTRACE_NIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); goto apply_bif_or_nif_epilogue; OpCase(apply_bif): @@ -3197,6 +3415,8 @@ void process_main(void) c_p->arity = 0; /* To allow garbage collection on ourselves * (check_process_code/2). */ + DTRACE_BIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); + SWAPOUT; c_p->fcalls = FCALLS - 1; vbf = (BifFunction) Arg(0); @@ -3216,6 +3436,8 @@ void process_main(void) PROCESS_MAIN_CHK_LOCKS(c_p); } + DTRACE_BIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); + apply_bif_or_nif_epilogue: ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); ERTS_HOLE_CHECK(c_p); @@ -5899,6 +6121,12 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg) save_calls(p, ep); } +#ifdef USE_VM_CALL_PROBES + if (DTRACE_ENABLED(global_function_entry)) { + BeamInstr *fptr = (BeamInstr *) ep->address; + DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]); + } +#endif return ep->address; } @@ -5948,6 +6176,12 @@ fixed_apply(Process* p, Eterm* reg, Uint arity) save_calls(p, ep); } +#ifdef USE_VM_CALL_PROBES + if (DTRACE_ENABLED(global_function_entry)) { + BeamInstr *fptr = (BeamInstr *) ep->address; + DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]); + } +#endif return ep->address; } @@ -5997,6 +6231,15 @@ erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* re c_p->max_arg_reg = sizeof(c_p->def_arg_reg)/sizeof(c_p->def_arg_reg[0]); } +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_hibernate)) { + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); + dtrace_fun_decode(c_p, module, function, arity, + process_name, mfa); + DTRACE2(process_hibernate, process_name, mfa); + } +#endif /* * Arrange for the process to be resumed at the given MFA with * the stack cleared. @@ -6072,6 +6315,9 @@ call_fun(Process* p, /* Current process. */ actual_arity = (int) code_ptr[-1]; if (actual_arity == arity+num_free) { + DTRACE_LOCAL_CALL(p, (Eterm)code_ptr[-3], + (Eterm)code_ptr[-2], + code_ptr[-1]); if (num_free == 0) { return code_ptr; } else { @@ -6089,7 +6335,7 @@ call_fun(Process* p, /* Current process. */ } else { /* * Something wrong here. First build a list of the arguments. - */ + */ if (is_non_value(args)) { Uint sz = 2 * arity; @@ -6164,6 +6410,7 @@ call_fun(Process* p, /* Current process. */ actual_arity = (int) ep->code[2]; if (arity == actual_arity) { + DTRACE_GLOBAL_CALL(p, ep->code[0], ep->code[1], (Uint)ep->code[2]); return ep->address; } else { /* @@ -6239,6 +6486,7 @@ call_fun(Process* p, /* Current process. */ reg[1] = function; reg[2] = args; } + DTRACE_GLOBAL_CALL(p, module, function, arity); return ep->address; } else { badfun: diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index ebdca87f4a..39d4582435 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -563,7 +563,11 @@ erts_queue_monitor_message(Process *p, ref_copy = copy_struct(ref, ref_size, &hp, ohp); tup = TUPLE5(hp, am_DOWN, ref_copy, type, item_copy, reason_copy); - erts_queue_message(p, p_locksp, bp, tup, NIL); + erts_queue_message(p, p_locksp, bp, tup, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); } static BIF_RETTYPE @@ -1944,7 +1948,11 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) { if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) save_calls(p, &exp_send); - if (SEQ_TRACE_TOKEN(p) != NIL) { + if (SEQ_TRACE_TOKEN(p) != NIL +#ifdef USE_VM_PROBES + && SEQ_TRACE_TOKEN(p) != am_have_dt_utag +#endif + ) { seq_trace_update_send(p); seq_trace_output(SEQ_TRACE_TOKEN(p), msg, SEQ_TRACE_SEND, portid, p); @@ -4226,13 +4234,21 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) for (i = 0; i < erts_max_processes; i++) { if (process_tab[i] != (Process*) 0) { Process* p = process_tab[i]; +#ifdef USE_VM_PROBES + p->seq_trace_token = (p->dt_utag != NIL) ? am_have_dt_utag : NIL; +#else p->seq_trace_token = NIL; +#endif p->seq_trace_clock = 0; p->seq_trace_lastcnt = 0; ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p); mp = p->msg.first; while(mp != NULL) { +#ifdef USE_VM_PROBES + ERL_MESSAGE_TOKEN(mp) = (ERL_MESSAGE_DT_UTAG(mp) != NIL) ? am_have_dt_utag : NIL; +#else ERL_MESSAGE_TOKEN(mp) = NIL; +#endif mp = mp->next; } } @@ -4630,3 +4646,193 @@ BIF_RETTYPE get_module_info_2(BIF_ALIST_2) } BIF_RET(ret); } + +BIF_RETTYPE dt_put_tag_1(BIF_ALIST_1) +{ +#ifdef USE_VM_PROBES + Eterm otag; + if (BIF_ARG_1 == am_undefined) { + otag = (DT_UTAG(BIF_P) == NIL) ? am_undefined : DT_UTAG(BIF_P); + DT_UTAG(BIF_P) = NIL; + DT_UTAG_FLAGS(BIF_P) = 0; + if (SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag) { + SEQ_TRACE_TOKEN(BIF_P) = NIL; + } + BIF_RET(otag); + } + if (!is_binary(BIF_ARG_1)) { + BIF_ERROR(BIF_P,BADARG); + } + otag = (DT_UTAG(BIF_P) == NIL) ? am_undefined : DT_UTAG(BIF_P); + DT_UTAG(BIF_P) = BIF_ARG_1; + DT_UTAG_FLAGS(BIF_P) |= DT_UTAG_PERMANENT; + if (SEQ_TRACE_TOKEN(BIF_P) == NIL) { + SEQ_TRACE_TOKEN(BIF_P) = am_have_dt_utag; + } + BIF_RET(otag); +#else + BIF_RET(am_undefined); +#endif +} + +BIF_RETTYPE dt_get_tag_0(BIF_ALIST_0) +{ +#ifdef USE_VM_PROBES + BIF_RET((DT_UTAG(BIF_P) == NIL || !(DT_UTAG_FLAGS(BIF_P) & DT_UTAG_PERMANENT)) ? am_undefined : DT_UTAG(BIF_P)); +#else + BIF_RET(am_undefined); +#endif +} +BIF_RETTYPE dt_get_tag_data_0(BIF_ALIST_0) +{ +#ifdef USE_VM_PROBES + BIF_RET((DT_UTAG(BIF_P) == NIL) ? am_undefined : DT_UTAG(BIF_P)); +#else + BIF_RET(am_undefined); +#endif +} +BIF_RETTYPE dt_prepend_vm_tag_data_1(BIF_ALIST_1) +{ +#ifdef USE_VM_PROBES + Eterm b; + Eterm *hp; + hp = HAlloc(BIF_P,2); + if (is_binary((DT_UTAG(BIF_P)))) { + Uint sz = binary_size(DT_UTAG(BIF_P)); + int i; + unsigned char *p,*q; + byte *temp_alloc = NULL; + b = new_binary(BIF_P,NULL,sz+1); + q = binary_bytes(b); + p = erts_get_aligned_binary_bytes(DT_UTAG(BIF_P),&temp_alloc); + for(i=0;i<sz;++i) { + q[i] = p[i]; + } + erts_free_aligned_binary_bytes(temp_alloc); + q[sz] = '\0'; + } else { + b = new_binary(BIF_P,(byte *)"\0",1); + } + BIF_RET(CONS(hp,b,BIF_ARG_1)); +#else + BIF_RET(BIF_ARG_1); +#endif +} +BIF_RETTYPE dt_append_vm_tag_data_1(BIF_ALIST_1) +{ +#ifdef USE_VM_PROBES + Eterm b; + Eterm *hp; + hp = HAlloc(BIF_P,2); + if (is_binary((DT_UTAG(BIF_P)))) { + Uint sz = binary_size(DT_UTAG(BIF_P)); + int i; + unsigned char *p,*q; + byte *temp_alloc = NULL; + b = new_binary(BIF_P,NULL,sz+1); + q = binary_bytes(b); + p = erts_get_aligned_binary_bytes(DT_UTAG(BIF_P),&temp_alloc); + for(i=0;i<sz;++i) { + q[i] = p[i]; + } + erts_free_aligned_binary_bytes(temp_alloc); + q[sz] = '\0'; + } else { + b = new_binary(BIF_P,(byte *)"\0",1); + } + BIF_RET(CONS(hp,BIF_ARG_1,b)); +#else + BIF_RET(BIF_ARG_1); +#endif +} +BIF_RETTYPE dt_spread_tag_1(BIF_ALIST_1) +{ +#ifdef USE_VM_PROBES + Eterm ret; + Eterm *hp; +#endif + if (BIF_ARG_1 != am_true && BIF_ARG_1 != am_false) { + BIF_ERROR(BIF_P,BADARG); + } +#ifdef USE_VM_PROBES + hp = HAlloc(BIF_P,3); + ret = TUPLE2(hp,make_small(DT_UTAG_FLAGS(BIF_P)),DT_UTAG(BIF_P)); + if (DT_UTAG(BIF_P) != NIL) { + if (BIF_ARG_1 == am_true) { + DT_UTAG_FLAGS(BIF_P) |= DT_UTAG_SPREADING; +#ifdef DTRACE_TAG_HARDDEBUG + erts_fprintf(stderr, + "Dtrace -> (%T) start spreading tag %T\r\n", + BIF_P->id,DT_UTAG(BIF_P)); +#endif + } else { + DT_UTAG_FLAGS(BIF_P) &= ~DT_UTAG_SPREADING; +#ifdef DTRACE_TAG_HARDDEBUG + erts_fprintf(stderr, + "Dtrace -> (%T) stop spreading tag %T\r\n", + BIF_P->id,DT_UTAG(BIF_P)); +#endif + } + } + BIF_RET(ret); +#else + BIF_RET(am_true); +#endif +} +BIF_RETTYPE dt_restore_tag_1(BIF_ALIST_1) +{ +#ifdef USE_VM_PROBES + Eterm *tpl; + Uint x; + if (is_not_tuple(BIF_ARG_1)) { + BIF_ERROR(BIF_P,BADARG); + } + tpl = tuple_val(BIF_ARG_1); + if(arityval(*tpl) != 2 || is_not_small(tpl[1]) || (is_not_binary(tpl[2]) && tpl[2] != NIL)) { + BIF_ERROR(BIF_P,BADARG); + } + if (tpl[2] == NIL) { + if (DT_UTAG(BIF_P) != NIL) { +#ifdef DTRACE_TAG_HARDDEBUG + erts_fprintf(stderr, + "Dtrace -> (%T) restore Killing tag!\r\n", + BIF_P->id); +#endif + } + DT_UTAG(BIF_P) = NIL; + if (SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag) { + SEQ_TRACE_TOKEN(BIF_P) = NIL; + } + DT_UTAG_FLAGS(BIF_P) = 0; + } else { + x = unsigned_val(tpl[1]) & (DT_UTAG_SPREADING | DT_UTAG_PERMANENT); +#ifdef DTRACE_TAG_HARDDEBUG + + if (!(x & DT_UTAG_SPREADING) && (DT_UTAG_FLAGS(BIF_P) & + DT_UTAG_SPREADING)) { + erts_fprintf(stderr, + "Dtrace -> (%T) restore stop spreading " + "tag %T\r\n", + BIF_P->id, tpl[2]); + } else if ((x & DT_UTAG_SPREADING) && + !(DT_UTAG_FLAGS(BIF_P) & DT_UTAG_SPREADING)) { + erts_fprintf(stderr, + "Dtrace -> (%T) restore start spreading " + "tag %T\r\n",BIF_P->id,tpl[2]); + } +#endif + DT_UTAG_FLAGS(BIF_P) = x; + DT_UTAG(BIF_P) = tpl[2]; + if (SEQ_TRACE_TOKEN(BIF_P) == NIL) { + SEQ_TRACE_TOKEN(BIF_P) = am_have_dt_utag; + } + } +#else + if (BIF_ARG_1 != am_true) { + BIF_ERROR(BIF_P,BADARG); + } +#endif + BIF_RET(am_true); +} + + diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 007c884a6a..8a85e102d1 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -814,6 +814,23 @@ bif erlang:check_old_code/1 # bif erlang:universaltime_to_posixtime/1 bif erlang:posixtime_to_universaltime/1 + +# +# New in R15B01 +# + +# The dtrace BIF's are always present, but give dummy results if dynamic trace is not enabled in the build +bif erlang:dt_put_tag/1 +bif erlang:dt_get_tag/0 +bif erlang:dt_get_tag_data/0 +bif erlang:dt_spread_tag/1 +bif erlang:dt_restore_tag/1 + +# These are dummies even with enabled dynamic trace unless vm probes are enabled. +# They are also internal, for dtrace tags sent to the VM's own drivers (efile) +bif erlang:dt_prepend_vm_tag_data/1 +bif erlang:dt_append_vm_tag_data/1 + # # Obsolete # diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 1d968fb147..2c355fadfa 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -30,6 +30,7 @@ #include "big.h" #include "erl_binary.h" #include "erl_bits.h" +#include "dtrace-wrapper.h" #ifdef HYBRID MA_STACK_DECLARE(src); @@ -59,6 +60,14 @@ copy_object(Eterm obj, Process* to) Eterm* hp = HAlloc(to, size); Eterm res; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(copy_object)) { + DTRACE_CHARBUF(proc_name, 64); + + erts_snprintf(proc_name, sizeof(proc_name), "%T", to->id); + DTRACE2(copy_object, proc_name, size); + } +#endif res = copy_struct(obj, size, &hp, &to->off_heap); #ifdef DEBUG if (eq(obj, res) == 0) { @@ -213,6 +222,8 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) if (IS_CONST(obj)) return obj; + DTRACE1(copy_struct, (int32_t)sz); + hp = htop = *hpp; hbot = htop + sz; hstart = (char *)htop; diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index bee61e7273..802feaeb1c 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -42,6 +42,7 @@ #include "external.h" #include "erl_binary.h" #include "erl_thr_progress.h" +#include "dtrace-wrapper.h" /* Turn this on to get printouts of all distribution messages * which go on the line @@ -381,7 +382,11 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp) Eterm tup; Eterm *hp = erts_alloc_message_heap(3,&bp,&ohp,rp,&rp_locks); tup = TUPLE2(hp, am_nodedown, name); - erts_queue_message(rp, &rp_locks, bp, tup, NIL); + erts_queue_message(rp, &rp_locks, bp, tup, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); } erts_smp_proc_unlock(rp, rp_locks); } @@ -740,19 +745,50 @@ erts_dsig_send_msg(ErtsDSigData *dsdp, Eterm remote, Eterm message) Eterm token = NIL; Process *sender = dsdp->proc; int res; +#ifdef USE_VM_PROBES + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; + Uint msize = 0; + DTRACE_CHARBUF(node_name, 64); + DTRACE_CHARBUF(sender_name, 64); + DTRACE_CHARBUF(receiver_name, 64); +#endif UseTmpHeapNoproc(5); - if (SEQ_TRACE_TOKEN(sender) != NIL) { + if (SEQ_TRACE_TOKEN(sender) != NIL +#ifdef USE_VM_PROBES + && SEQ_TRACE_TOKEN(sender) != am_have_dt_utag +#endif + ) { seq_trace_update_send(sender); token = SEQ_TRACE_TOKEN(sender); seq_trace_output(token, message, SEQ_TRACE_SEND, remote, sender); } +#ifdef USE_VM_PROBES + *node_name = *sender_name = *receiver_name = '\0'; + if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) { + erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname); + erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id); + erts_snprintf(receiver_name, sizeof(receiver_name), "%T", remote); + msize = size_object(message); + if (token != NIL && token != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); + } + } +#endif if (token != NIL) ctl = TUPLE4(&ctl_heap[0], make_small(DOP_SEND_TT), am_Cookie, remote, token); else ctl = TUPLE3(&ctl_heap[0], make_small(DOP_SEND), am_Cookie, remote); + DTRACE6(message_send, sender_name, receiver_name, + msize, tok_label, tok_lastcnt, tok_serial); + DTRACE7(message_send_remote, sender_name, node_name, receiver_name, + msize, tok_label, tok_lastcnt, tok_serial); res = dsig_send(dsdp, ctl, message, 0); UnUseTmpHeapNoproc(5); return res; @@ -766,13 +802,41 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message) Eterm token = NIL; Process *sender = dsdp->proc; int res; +#ifdef USE_VM_PROBES + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; + Uint32 msize = 0; + DTRACE_CHARBUF(node_name, 64); + DTRACE_CHARBUF(sender_name, 64); + DTRACE_CHARBUF(receiver_name, 128); +#endif UseTmpHeapNoproc(6); - if (SEQ_TRACE_TOKEN(sender) != NIL) { + if (SEQ_TRACE_TOKEN(sender) != NIL +#ifdef USE_VM_PROBES + && SEQ_TRACE_TOKEN(sender) != am_have_dt_utag +#endif + ) { seq_trace_update_send(sender); token = SEQ_TRACE_TOKEN(sender); seq_trace_output(token, message, SEQ_TRACE_SEND, remote_name, sender); } +#ifdef USE_VM_PROBES + *node_name = *sender_name = *receiver_name = '\0'; + if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) { + erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname); + erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id); + erts_snprintf(receiver_name, sizeof(receiver_name), + "{%T,%s}", remote_name, node_name); + msize = size_object(message); + if (token != NIL && token != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); + } + } +#endif if (token != NIL) ctl = TUPLE5(&ctl_heap[0], make_small(DOP_REG_SEND_TT), @@ -780,6 +844,10 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message) else ctl = TUPLE4(&ctl_heap[0], make_small(DOP_REG_SEND), sender->id, am_Cookie, remote_name); + DTRACE6(message_send, sender_name, receiver_name, + msize, tok_label, tok_lastcnt, tok_serial); + DTRACE7(message_send_remote, sender_name, node_name, receiver_name, + msize, tok_label, tok_lastcnt, tok_serial); res = dsig_send(dsdp, ctl, message, 0); UnUseTmpHeapNoproc(6); return res; @@ -793,9 +861,23 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote, Eterm ctl; DeclareTmpHeapNoproc(ctl_heap,6); int res; +#ifdef USE_VM_PROBES + Process *sender = dsdp->proc; + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; + DTRACE_CHARBUF(node_name, 64); + DTRACE_CHARBUF(sender_name, 64); + DTRACE_CHARBUF(remote_name, 128); + DTRACE_CHARBUF(reason_str, 128); +#endif UseTmpHeapNoproc(6); - if (token != NIL) { + if (token != NIL +#ifdef USE_VM_PROBES + && token != am_have_dt_utag +#endif + ) { seq_trace_update_send(dsdp->proc); seq_trace_output_exit(token, reason, SEQ_TRACE_SEND, remote, local); ctl = TUPLE5(&ctl_heap[0], @@ -803,6 +885,23 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote, } else { ctl = TUPLE4(&ctl_heap[0], make_small(DOP_EXIT), local, remote, reason); } +#ifdef USE_VM_PROBES + *node_name = *sender_name = *remote_name = '\0'; + if (DTRACE_ENABLED(process_exit_signal_remote)) { + erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname); + erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id); + erts_snprintf(remote_name, sizeof(remote_name), + "{%T,%s}", remote, node_name); + erts_snprintf(reason_str, sizeof(reason), "%T", reason); + if (token != NIL && token != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); + } + } +#endif + DTRACE7(process_exit_signal_remote, sender_name, node_name, + remote_name, reason_str, tok_label, tok_lastcnt, tok_serial); /* forced, i.e ignore busy */ res = dsig_send(dsdp, ctl, THE_NON_VALUE, 1); UnUseTmpHeapNoproc(6); @@ -1619,6 +1718,18 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy) if (!(dep->qflgs & ERTS_DE_QFLG_BUSY)) { if (suspended) resume = 1; /* was busy when we started, but isn't now */ +#ifdef USE_VM_PROBES + if (resume && DTRACE_ENABLED(dist_port_not_busy)) { + DTRACE_CHARBUF(port_str, 64); + DTRACE_CHARBUF(remote_str, 64); + + erts_snprintf(port_str, sizeof(port_str), "%T", cid); + erts_snprintf(remote_str, sizeof(remote_str), + "%T", dep->sysname); + DTRACE3(dist_port_not_busy, erts_this_node_sysname, + port_str, remote_str); + } +#endif } else { /* Enqueue suspended process on dist entry */ @@ -1668,6 +1779,19 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy) } if (suspended) { +#ifdef USE_VM_PROBES + if (!resume && DTRACE_ENABLED(dist_port_busy)) { + DTRACE_CHARBUF(port_str, 64); + DTRACE_CHARBUF(remote_str, 64); + DTRACE_CHARBUF(pid_str, 16); + + erts_snprintf(port_str, sizeof(port_str), "%T", cid); + erts_snprintf(remote_str, sizeof(remote_str), "%T", dep->sysname); + erts_snprintf(pid_str, sizeof(pid_str), "%T", c_p->id); + DTRACE4(dist_port_busy, erts_this_node_sysname, + port_str, remote_str, pid_str); + } +#endif if (!resume && erts_system_monitor_flags.busy_dist_port) monitor_generic(c_p, am_busy_dist_port, cid); return ERTS_DSIG_SEND_YIELD; @@ -1691,6 +1815,18 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf) "(%beu bytes) passed.\n", size); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(dist_output)) { + DTRACE_CHARBUF(port_str, 64); + DTRACE_CHARBUF(remote_str, 64); + + erts_snprintf(port_str, sizeof(port_str), "%T", prt->id); + erts_snprintf(remote_str, sizeof(remote_str), + "%T", prt->dist_entry->sysname); + DTRACE4(dist_output, erts_this_node_sysname, port_str, + remote_str, size); + } +#endif prt->caller = NIL; fpe_was_unmasked = erts_block_fpe(); (*prt->drv_ptr->output)((ErlDrvData) prt->drv_data, @@ -1733,6 +1869,18 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf) ASSERT(prt->drv_ptr->outputv); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(dist_outputv)) { + DTRACE_CHARBUF(port_str, 64); + DTRACE_CHARBUF(remote_str, 64); + + erts_snprintf(port_str, sizeof(port_str), "%T", prt->id); + erts_snprintf(remote_str, sizeof(remote_str), + "%T", prt->dist_entry->sysname); + DTRACE4(dist_outputv, erts_this_node_sysname, port_str, + remote_str, size); + } +#endif prt->caller = NIL; fpe_was_unmasked = erts_block_fpe(); (*prt->drv_ptr->outputv)((ErlDrvData) prt->drv_data, &eiov); @@ -2052,6 +2200,18 @@ erts_dist_command(Port *prt, int reds_limit) void erts_dist_port_not_busy(Port *prt) { +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(dist_port_not_busy)) { + DTRACE_CHARBUF(port_str, 64); + DTRACE_CHARBUF(remote_str, 64); + + erts_snprintf(port_str, sizeof(port_str), "%T", prt->id); + erts_snprintf(remote_str, sizeof(remote_str), + "%T", prt->dist_entry->sysname); + DTRACE3(dist_port_not_busy, erts_this_node_sysname, + port_str, remote_str); + } +#endif erts_schedule_dist_command(prt, NULL); } @@ -2972,7 +3132,11 @@ send_nodes_mon_msg(Process *rp, } ASSERT(hend == hp); - erts_queue_message(rp, rp_locksp, bp, msg, NIL); + erts_queue_message(rp, rp_locksp, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); } static void @@ -2985,6 +3149,21 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas ASSERT(is_immed(what)); ASSERT(is_immed(node)); ASSERT(is_immed(type)); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(dist_monitor)) { + DTRACE_CHARBUF(what_str, 12); + DTRACE_CHARBUF(node_str, 64); + DTRACE_CHARBUF(type_str, 12); + DTRACE_CHARBUF(reason_str, 64); + + erts_snprintf(what_str, sizeof(what_str), "%T", what); + erts_snprintf(node_str, sizeof(node_str), "%T", node); + erts_snprintf(type_str, sizeof(type_str), "%T", type); + erts_snprintf(reason_str, sizeof(reason_str), "%T", reason); + DTRACE5(dist_monitor, erts_this_node_sysname, + what_str, node_str, type_str, reason_str); + } +#endif ERTS_SMP_LC_ASSERT(!c_p || (erts_proc_lc_my_proc_locks(c_p) diff --git a/erts/emulator/beam/dtrace-wrapper.h b/erts/emulator/beam/dtrace-wrapper.h new file mode 100644 index 0000000000..9d1e55fc43 --- /dev/null +++ b/erts/emulator/beam/dtrace-wrapper.h @@ -0,0 +1,109 @@ +/* + * %CopyrightBegin% + * + * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 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 + * 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% + */ + +#ifndef __DTRACE_WRAPPER_H +#define __DTRACE_WRAPPER_H + +#define DTRACE_TERM_BUF_SIZE 256 + +/* + * Some varieties of SystemTap macros do not like statically-sized + * char[N] buffers. (For example, CentOS 6's macros.) + * So, we'll play a game to humor them. + * + * The code necessary to play nice with CentOS 6's SystemTap looks + * stupid to a C programmer's eyes, so we hide the ugliness with this + * macro, which expands: + * + * DTRACE_CHARBUF(proc_name, 64); + * + * to become: + * + * char proc_name_BUFFER[64], *proc_name = proc_name_BUFFER; + */ + +#define DTRACE_CHARBUF(name, size) \ + char name##_BUFFER[size], *name = name##_BUFFER + +#if defined(USE_DYNAMIC_TRACE) && defined(USE_VM_PROBES) + +#include "erlang_dtrace.h" + +#define DTRACE_ENABLED(name) \ + erlang_##name##_enabled() +#define DTRACE0(name) \ + erlang_##name() +#define DTRACE1(name, a0) \ + erlang_##name(a0) +#define DTRACE2(name, a0, a1) \ + erlang_##name((a0), (a1)) +#define DTRACE3(name, a0, a1, a2) \ + erlang_##name((a0), (a1), (a2)) +#define DTRACE4(name, a0, a1, a2, a3) \ + erlang_##name((a0), (a1), (a2), (a3)) +#define DTRACE5(name, a0, a1, a2, a3, a4) \ + erlang_##name((a0), (a1), (a2), (a3), (a4)) +#define DTRACE6(name, a0, a1, a2, a3, a4, a5) \ + erlang_##name((a0), (a1), (a2), (a3), (a4), (a5)) +#define DTRACE7(name, a0, a1, a2, a3, a4, a5, a6) \ + erlang_##name((a0), (a1), (a2), (a3), (a4), (a5), (a6)) +#define DTRACE10(name, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \ + erlang_##name((a0), (a1), (a2), (a3), (a4), (a5), (a6), (a7), (a8), (a9)) +#define DTRACE11(name, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \ + erlang_##name((a0), (a1), (a2), (a3), (a4), (a5), (a6), (a7), (a8), (a9), (a10)) + +#if defined(_SDT_PROBE) && !defined(STAP_PROBE11) +/* SLF: This is Ubuntu 11-style SystemTap hackery */ +/* work arround for missing STAP macro */ +#define STAP_PROBE11(provider,name,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11) \ + _SDT_PROBE(provider, name, 11, \ + (arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11)) +#define _SDT_ASM_OPERANDS_11(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11) \ + _SDT_ASM_OPERANDS_10(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,arg10), \ + _SDT_ARG(11, arg11) +#endif + +#ifdef STAP_PROBE_ADDR +/* SLF: This is CentOS 5-style SystemTap hackery */ +/* SystemTap compat mode cannot support 11 args. We'll ignore the 11th */ +#define STAP_PROBE11(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8,parm9,parm10,parm11) \ + STAP_PROBE10(provider,probe,(parm1),(parm2),(parm3),(parm4),(parm5),(parm6),(parm7),(parm8),(parm9),(parm10)) +#endif /* STAP_PROBE_ADDR */ + +#else /* USE_DYNAMIC_TRACE && USE_VM_PROBES */ + +/* Render all macros to do nothing */ +#define DTRACE_ENABLED(name) 0 +#define DTRACE0(name) do {} while (0) +#define DTRACE1(name, a0) do {} while (0) +#define DTRACE2(name, a0, a1) do {} while (0) +#define DTRACE3(name, a0, a1, a2) do {} while (0) +#define DTRACE4(name, a0, a1, a2, a3) do {} while (0) +#define DTRACE5(name, a0, a1, a2, a3, a4) do {} while (0) +#define DTRACE6(name, a0, a1, a2, a3, a4, a5) do {} while (0) +#define DTRACE7(name, a0, a1, a2, a3, a4, a5, a6) do {} while (0) +#define DTRACE10(name, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \ + do {} while (0) +#define DTRACE11(name, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \ + do {} while (0) + +#endif /* USE_DYNAMIC_TRACE && USE_VM_PROBES */ + +#endif /* __DTRACE_WRAPPER_H */ diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index df27186680..8130d5c576 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -3001,7 +3001,11 @@ reply_alloc_info(void *vair) HRelease(rp, hp_end, hp); } - erts_queue_message(rp, &rp_locks, bp, msg, NIL); + erts_queue_message(rp, &rp_locks, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); if (air->req_sched == sched_id) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index 8bca9ae582..f0e98b33a5 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -26,6 +26,7 @@ #include "erl_threads.h" #include "erl_thr_queue.h" #include "erl_async.h" +#include "dtrace-wrapper.h" #define ERTS_MAX_ASYNC_READY_CALLS_IN_SEQ 20 @@ -121,6 +122,14 @@ typedef struct { #endif } ErtsAsyncData; +/* + * Some compilers, e.g. GCC 4.2.1 and -O3, will optimize away DTrace + * calls if they're the last thing in the function. :-( + * Many thanks to Trond Norbye, via: + * https://github.com/memcached/memcached/commit/6298b3978687530bc9d219b6ac707a1b681b2a46 + */ +static unsigned gcc_optimizer_hack = 0; + int erts_async_max_threads; /* Initialized by erl_init.c */ int erts_async_thread_suggested_stack_size; /* Initialized by erl_init.c */ @@ -244,6 +253,8 @@ erts_get_async_ready_queue(Uint sched_id) static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q) { + int len; + if (is_internal_port(a->port)) { #if ERTS_USE_ASYNC_READY_Q ErtsAsyncReadyQ *arq = async_ready_q(a->sched_id); @@ -259,6 +270,17 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q) #endif erts_thr_q_enqueue(&q->thr_q, a); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(aio_pool_add)) { + DTRACE_CHARBUF(port_str, 16); + + erts_snprintf(port_str, sizeof(port_str), "%T", a->port); + /* DTRACE TODO: Get the queue length from erts_thr_q_enqueue() ? */ + len = -1; + DTRACE2(aio_pool_add, port_str, len); + } +#endif + gcc_optimizer_hack++; } static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, @@ -269,6 +291,7 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, int saved_fin_deq = 0; ErtsThrQFinDeQ_t fin_deq; #endif + int len; while (1) { ErtsAsync *a = (ErtsAsync *) erts_thr_q_dequeue(q); @@ -280,7 +303,16 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, if (saved_fin_deq) erts_thr_q_append_finalize_dequeue_data(&a->q.fin_deq, &fin_deq); #endif +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(aio_pool_get)) { + DTRACE_CHARBUF(port_str, 16); + erts_snprintf(port_str, sizeof(port_str), "%T", a->port); + /* DTRACE TODO: Get the length from erts_thr_q_dequeue() ? */ + len = -1; + DTRACE2(aio_pool_get, port_str, len); + } +#endif return a; } diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index 37d540b41b..bcfdacb91c 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -45,6 +45,7 @@ #include "big.h" #include "dist.h" #include "erl_version.h" +#include "dtrace-wrapper.h" #ifdef ERTS_SMP #define DDLL_SMP 1 @@ -1647,6 +1648,7 @@ static int do_unload_driver_entry(DE_Handle *dh, Eterm *save_name) diver_list lock here!*/ if (q->finish) { int fpe_was_unmasked = erts_block_fpe(); + DTRACE1(driver_finish, q->name); (*(q->finish))(); erts_unblock_fpe(fpe_was_unmasked); } @@ -1760,7 +1762,11 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type, hp += REF_THING_SIZE; mess = TUPLE5(hp,type,r,am_driver,driver_name,tag); } - erts_queue_message(proc, &rp_locks, bp, mess, am_undefined); + erts_queue_message(proc, &rp_locks, bp, mess, am_undefined +#ifdef USE_VM_PROBES + , NIL +#endif + ); erts_smp_proc_unlock(proc, rp_locks); ERTS_SMP_CHK_NO_PROC_LOCKS; } diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index ebd475f73a..041eac240d 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -115,6 +115,12 @@ static char erts_system_version[] = ("Erlang " ERLANG_OTP_RELEASE #ifdef VALGRIND " [valgrind-compiled]" #endif +#ifdef USE_DTRACE + " [dtrace]" +#endif +#ifdef USE_SYSTEMTAP + " [systemtap]" +#endif "\n"); #define ASIZE(a) (sizeof(a)/sizeof(a[0])) @@ -2720,6 +2726,24 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) #endif BIF_RET(am_true); } + else if (ERTS_IS_ATOM_STR("dynamic_trace", BIF_ARG_1)) { +#if defined(USE_DTRACE) + DECL_AM(dtrace); + BIF_RET(AM_dtrace); +#elif defined(USE_SYSTEMTAP) + DECL_AM(systemtap); + BIF_RET(AM_systemtap); +#else + BIF_RET(am_none); +#endif + } + else if (ERTS_IS_ATOM_STR("dynamic_trace_probes", BIF_ARG_1)) { +#if defined(USE_VM_PROBES) + BIF_RET(am_true); +#else + BIF_RET(am_false); +#endif + } #ifdef ERTS_SMP else if (ERTS_IS_ATOM_STR("thread_progress", BIF_ARG_1)) { erts_thr_progress_dbg_print_state(); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index cd423eb200..3056319809 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -40,6 +40,7 @@ #include "external.h" #include "packet_parser.h" #include "erl_bits.h" +#include "dtrace-wrapper.h" static int open_port(Process* p, Eterm name, Eterm settings, int *err_nump); static byte* convert_environment(Process* p, Eterm env); @@ -343,6 +344,16 @@ port_call(Process* c_p, Eterm arg1, Eterm arg2, Eterm arg3) __FILE__, __LINE__, endp - (bytes + size)); } erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_call)) { + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); + + dtrace_pid_str(p->connected, process_str); + dtrace_port_str(p, port_str); + DTRACE5(driver_call, process_str, port_str, p->name, op, real_size); + } +#endif prc = (char *) port_resp; fpe_was_unmasked = erts_block_fpe(); ret = drv->call((ErlDrvData)p->drv_data, @@ -539,6 +550,18 @@ BIF_RETTYPE port_connect_2(BIF_ALIST_2) prt->connected = pid; /* internal pid */ erts_smp_port_unlock(prt); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(port_connect)) { + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE); + + dtrace_pid_str(prt->connected, process_str); + erts_snprintf(port_str, sizeof(port_str), "%T", prt->id); + dtrace_proc_str(rp, newprocess_str); + DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str); + } +#endif BIF_RET(am_true); } @@ -904,7 +927,16 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump) erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN); port_num = erts_open_driver(driver, p->id, name_buf, &opts, err_nump); +#ifdef USE_VM_PROBES + if (port_num >= 0 && DTRACE_ENABLED(port_open)) { + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); + dtrace_proc_str(p, process_str); + erts_snprintf(port_str, sizeof(port_str), "%T", erts_port[port_num].id); + DTRACE3(port_open, process_str, name_buf, port_str); + } +#endif erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN); if (port_num < 0) { diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c index a922a33da3..0002f8374f 100644 --- a/erts/emulator/beam/erl_bif_timer.c +++ b/erts/emulator/beam/erl_bif_timer.c @@ -373,7 +373,11 @@ bif_timer_timeout(ErtsBifTimer* btm) message = TUPLE3(hp, am_timeout, ref, message); } - erts_queue_message(rp, &rp_locks, bp, message, NIL); + erts_queue_message(rp, &rp_locks, bp, message, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); erts_smp_proc_unlock(rp, rp_locks); erts_smp_proc_dec_refc(rp); } diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index b0a58c80ea..1ef4b07c24 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -1744,9 +1744,17 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2, return THE_NON_VALUE; } if (build_result) { +#ifdef USE_VM_PROBES + old_value = (SEQ_TRACE_TOKEN(p) == am_have_dt_utag) ? NIL : SEQ_TRACE_TOKEN(p); +#else old_value = SEQ_TRACE_TOKEN(p); +#endif } +#ifdef USE_VM_PROBES + SEQ_TRACE_TOKEN(p) = (DT_UTAG(p) != NIL) ? am_have_dt_utag : NIL; +#else SEQ_TRACE_TOKEN(p) = NIL; +#endif return old_value; } else { @@ -1759,7 +1767,11 @@ new_seq_trace_token(Process* p) { Eterm* hp; - if (SEQ_TRACE_TOKEN(p) == NIL) { + if (SEQ_TRACE_TOKEN(p) == NIL +#ifdef USE_VM_PROBES + || SEQ_TRACE_TOKEN(p) == am_have_dt_utag +#endif + ) { hp = HAlloc(p, 6); SEQ_TRACE_TOKEN(p) = TUPLE5(hp, make_small(0), /* Flags */ make_small(0), /* Label */ @@ -1779,7 +1791,11 @@ BIF_RETTYPE erl_seq_trace_info(Process *p, Eterm item) BIF_ERROR(p, BADARG); } - if (SEQ_TRACE_TOKEN(p) == NIL) { + if (SEQ_TRACE_TOKEN(p) == NIL +#ifdef USE_VM_PROBES + || SEQ_TRACE_TOKEN(p) == am_have_dt_utag +#endif + ) { if ((item == am_send) || (item == am_receive) || (item == am_print) || (item == am_timestamp)) { hp = HAlloc(p,3); @@ -1836,8 +1852,13 @@ BIF_RETTYPE seq_trace_info_1(BIF_ALIST_1) */ BIF_RETTYPE seq_trace_print_1(BIF_ALIST_1) { - if (SEQ_TRACE_TOKEN(BIF_P) == NIL) + if (SEQ_TRACE_TOKEN(BIF_P) == NIL +#ifdef USE_VM_PROBES + || SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag +#endif + ) { BIF_RET(am_false); + } seq_trace_update_send(BIF_P); seq_trace_output(SEQ_TRACE_TOKEN(BIF_P), BIF_ARG_1, SEQ_TRACE_PRINT, NIL, BIF_P); @@ -1854,8 +1875,13 @@ BIF_RETTYPE seq_trace_print_1(BIF_ALIST_1) */ BIF_RETTYPE seq_trace_print_2(BIF_ALIST_2) { - if (SEQ_TRACE_TOKEN(BIF_P) == NIL) + if (SEQ_TRACE_TOKEN(BIF_P) == NIL +#ifdef USE_VM_PROBES + || SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag +#endif + ) { BIF_RET(am_false); + } if (!(is_atom(BIF_ARG_1) || is_small(BIF_ARG_1))) { BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 4821a7d9fb..be345e7c9b 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -2203,7 +2203,11 @@ restart: *esp++ = am_true; break; case matchIsSeqTrace: - if (SEQ_TRACE_TOKEN(c_p) != NIL) + if (SEQ_TRACE_TOKEN(c_p) != NIL +#ifdef USE_VM_PROBES + && SEQ_TRACE_TOKEN(c_p) != am_have_dt_utag +#endif + ) *esp++ = am_true; else *esp++ = am_false; @@ -2227,7 +2231,11 @@ restart: --esp; break; case matchGetSeqToken: - if (SEQ_TRACE_TOKEN(c_p) == NIL) + if (SEQ_TRACE_TOKEN(c_p) == NIL +#ifdef USE_VM_PROBES + || SEQ_TRACE_TOKEN(c_p) == am_have_dt_utag +#endif + ) *esp++ = NIL; else { Eterm sender = SEQ_TRACE_TOKEN_SENDER(c_p); diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 7510f6b724..1ae9a211d7 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -649,6 +649,8 @@ EXTERN int erl_drv_getenv(char *key, char *value, size_t *value_size); #endif +/* also in global.h, but driver's can't include global.h */ +void dtrace_drvport_str(ErlDrvPort port, char *port_buf); diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index bde87b8346..82f2dc6091 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -35,6 +35,7 @@ #include "hipe_stack.h" #include "hipe_mode_switch.h" #endif +#include "dtrace-wrapper.h" #define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1 #define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20 @@ -349,7 +350,9 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) Uint reclaimed_now = 0; int done = 0; Uint ms1, s1, us1; - +#ifdef USE_VM_PROBES + DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); +#endif if (IS_TRACED_FL(p, F_TRACE_GC)) { trace_gc(p, am_gc_start); } @@ -369,15 +372,27 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) if (GEN_GCS(p) >= MAX_GEN_GCS(p)) { FLAGS(p) |= F_NEED_FULLSWEEP; } - +#ifdef USE_VM_PROBES + *pidbuf = '\0'; + if (DTRACE_ENABLED(gc_major_start) + || DTRACE_ENABLED(gc_major_end) + || DTRACE_ENABLED(gc_minor_start) + || DTRACE_ENABLED(gc_minor_end)) { + dtrace_proc_str(p, pidbuf); + } +#endif /* * Test which type of GC to do. */ while (!done) { if ((FLAGS(p) & F_NEED_FULLSWEEP) != 0) { + DTRACE2(gc_major_start, pidbuf, need); done = major_collection(p, need, objv, nobj, &reclaimed_now); + DTRACE2(gc_major_end, pidbuf, reclaimed_now); } else { + DTRACE2(gc_minor_start, pidbuf, need); done = minor_collection(p, need, objv, nobj, &reclaimed_now); + DTRACE2(gc_minor_end, pidbuf, reclaimed_now); } } @@ -1118,6 +1133,15 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) sys_memcpy(n_heap + new_sz - n, p->stop, n * sizeof(Eterm)); p->stop = n_heap + new_sz - n; +#ifdef USE_VM_PROBES + if (HEAP_SIZE(p) != new_sz && DTRACE_ENABLED(process_heap_grow)) { + DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, pidbuf); + DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz); + } +#endif + ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, (void*)HEAP_START(p), HEAP_SIZE(p) * sizeof(Eterm)); @@ -1339,6 +1363,15 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) sys_memcpy(n_heap + new_sz - n, p->stop, n * sizeof(Eterm)); p->stop = n_heap + new_sz - n; +#ifdef USE_VM_PROBES + if (HEAP_SIZE(p) != new_sz && DTRACE_ENABLED(process_heap_grow)) { + DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, pidbuf); + DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz); + } +#endif + ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, (void *) HEAP_START(p), (HEAP_END(p) - HEAP_START(p)) * sizeof(Eterm)); @@ -1907,7 +1940,13 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) roots[n].sz = 1; n++; } - +#ifdef USE_VM_PROBES + if (is_not_immed(p->dt_utag)) { + roots[n].v = &p->dt_utag; + roots[n].sz = 1; + n++; + } +#endif ASSERT(is_nil(p->tracer_proc) || is_internal_pid(p->tracer_proc) || is_internal_port(p->tracer_proc)); @@ -2009,6 +2048,16 @@ grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj) HEAP_TOP(p) = new_heap + heap_size; HEAP_START(p) = new_heap; } + +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_heap_grow)) { + DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, pidbuf); + DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz); + } +#endif + HEAP_SIZE(p) = new_sz; } @@ -2018,7 +2067,6 @@ shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj) Eterm* new_heap; Uint heap_size = HEAP_TOP(p) - HEAP_START(p); Sint offs; - Uint stack_size = p->hend - p->stop; ASSERT(new_sz < p->heap_sz); @@ -2047,6 +2095,16 @@ shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj) HEAP_TOP(p) = new_heap + heap_size; HEAP_START(p) = new_heap; } + +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_heap_shrink)) { + DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, pidbuf); + DTRACE3(process_heap_shrink, pidbuf, HEAP_SIZE(p), new_sz); + } +#endif + HEAP_SIZE(p) = new_sz; } @@ -2429,6 +2487,13 @@ offset_mqueue(Process *p, Sint offs, char* area, Uint area_size) if (is_boxed(mesg) && in_area(ptr_val(mesg), area, area_size)) { ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs); } +#ifdef USE_VM_PROBES + mesg = ERL_MESSAGE_DT_UTAG(mp); + if (is_boxed(mesg) && in_area(ptr_val(mesg), area, area_size)) { + ERL_MESSAGE_DT_UTAG(mp) = offset_ptr(mesg, offs); + } +#endif + ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) || is_tuple(ERL_MESSAGE_TOKEN(mp)) || is_atom(ERL_MESSAGE_TOKEN(mp)))); @@ -2448,6 +2513,9 @@ offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size, offset_heap_ptr(&p->fvalue, 1, offs, area, area_size); offset_heap_ptr(&p->ftrace, 1, offs, area, area_size); offset_heap_ptr(&p->seq_trace_token, 1, offs, area, area_size); +#ifdef USE_VM_PROBES + offset_heap_ptr(&p->dt_utag, 1, offs, area, area_size); +#endif offset_heap_ptr(&p->group_leader, 1, offs, area, area_size); offset_mqueue(p, offs, area, area_size); offset_heap_ptr(p->stop, (STACK_START(p) - p->stop), offs, area, area_size); diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 09e85893c3..5eb2a69242 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -183,6 +183,9 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "save_ops_lock", NULL }, #endif #endif +#ifdef USE_VM_PROBES + { "efile_drv dtrace mutex", NULL }, +#endif { "mtrace_buf", NULL }, { "erts_alloc_hard_debug", NULL } }; diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index ab1ab7b1ea..4cdf2d7d09 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -31,6 +31,7 @@ #include "erl_process.h" #include "erl_nmgc.h" #include "erl_binary.h" +#include "dtrace-wrapper.h" ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message, ErlMessage, @@ -335,6 +336,11 @@ erts_queue_dist_message(Process *rcvr, Eterm token) { ErlMessage* mp; +#ifdef USE_VM_PROBES + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; +#endif #ifdef ERTS_SMP ErtsProcLocks need_locks; #endif @@ -376,15 +382,61 @@ erts_queue_dist_message(Process *rcvr, message_free(mp); msg = erts_msg_distext2heap(rcvr, rcvr_locks, &mbuf, &token, dist_ext); if (is_value(msg)) - erts_queue_message(rcvr, rcvr_locks, mbuf, msg, token); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(message_queued)) { + DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(rcvr, receiver_name); + if (token != NIL && token != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); + } + DTRACE6(message_queued, + receiver_name, size_object(msg), rcvr->msg.len, + tok_label, tok_lastcnt, tok_serial); + } +#endif + erts_queue_message(rcvr, rcvr_locks, mbuf, msg, token +#ifdef USE_VM_PROBES + , NIL +#endif + ); } else { /* Enqueue message on external format */ ERL_MESSAGE_TERM(mp) = THE_NON_VALUE; - ERL_MESSAGE_TOKEN(mp) = token; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = NIL; + if (token == am_have_dt_utag) { + ERL_MESSAGE_TOKEN(mp) = NIL; + } else { +#endif + ERL_MESSAGE_TOKEN(mp) = token; +#ifdef USE_VM_PROBES + } +#endif mp->next = NULL; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(message_queued)) { + DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(rcvr, receiver_name); + if (token != NIL && token != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); + } + /* + * TODO: We don't know the real size of the external message here. + * -1 will appear to a D script as 4294967295. + */ + DTRACE6(message_queued, receiver_name, -1, rcvr->msg.len + 1, + tok_label, tok_lastcnt, tok_serial); + } +#endif mp->data.dist_ext = dist_ext; LINK_MESSAGE(rcvr, mp); @@ -398,7 +450,11 @@ erts_queue_message(Process* receiver, ErtsProcLocks *receiver_locks, ErlHeapFragment* bp, Eterm message, - Eterm seq_trace_token) + Eterm seq_trace_token +#ifdef USE_VM_PROBES + , Eterm dt_utag +#endif +) { ErlMessage* mp; #ifdef ERTS_SMP @@ -439,6 +495,9 @@ erts_queue_message(Process* receiver, ERL_MESSAGE_TERM(mp) = message; ERL_MESSAGE_TOKEN(mp) = seq_trace_token; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = dt_utag; +#endif mp->next = NULL; mp->data.heap_frag = bp; @@ -462,12 +521,30 @@ erts_queue_message(Process* receiver, LINK_MESSAGE(receiver, mp); #endif +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(message_queued)) { + DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; + + dtrace_proc_str(receiver, receiver_name); + if (seq_trace_token != NIL && is_tuple(seq_trace_token)) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(seq_trace_token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token)); + } + DTRACE6(message_queued, + receiver_name, size_object(message), receiver->msg.len, + tok_label, tok_lastcnt, tok_serial); + } +#endif notify_new_message(receiver); if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) { trace_receive(receiver, message); } - + #ifndef ERTS_SMP ERTS_HOLE_CHECK(receiver); #endif @@ -497,6 +574,9 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg) Sint offs; Uint sz; ErlHeapFragment *bp; +#ifdef USE_VM_PROBES + Eterm utag; +#endif #ifdef HARD_DEBUG ProcBin *dbg_mso_start = off_heap->mso; @@ -506,32 +586,56 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg) ErlHeapFragment *dbg_bp; Uint *dbg_hp, *dbg_thp_start; Uint dbg_term_sz, dbg_token_sz; +#ifdef USE_VM_PROBES + Eterm dbg_utag; + Uint dbg_utag_sz; +#endif #endif bp = msg->data.heap_frag; term = ERL_MESSAGE_TERM(msg); token = ERL_MESSAGE_TOKEN(msg); +#ifdef USE_VM_PROBES + utag = ERL_MESSAGE_DT_UTAG(msg); +#endif if (!bp) { +#ifdef USE_VM_PROBES + ASSERT(is_immed(term) && is_immed(token) && is_immed(utag)); +#else ASSERT(is_immed(term) && is_immed(token)); +#endif return; } #ifdef HARD_DEBUG dbg_term_sz = size_object(term); dbg_token_sz = size_object(token); + dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz); +#ifdef USE_VM_PROBES + dbg_utag_sz = size_object(utag); + dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz + dbg_utag_sz ); +#endif /*ASSERT(dbg_term_sz + dbg_token_sz == erts_msg_used_frag_sz(msg)); Copied size may be smaller due to removed SubBins's or garbage. Copied size may be larger due to duplicated shared terms. */ - dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz); dbg_hp = dbg_bp->mem; dbg_term = copy_struct(term, dbg_term_sz, &dbg_hp, &dbg_bp->off_heap); dbg_token = copy_struct(token, dbg_token_sz, &dbg_hp, &dbg_bp->off_heap); - dbg_thp_start = *hpp; +#ifdef USE_VM_PROBES + dbg_utag = copy_struct(utag, dbg_utag_sz, &dbg_hp, &dbg_bp->off_heap); +#endif + dbg_thp_start = *hpp; #endif if (bp->next != NULL) { - move_multi_frags(hpp, off_heap, bp, msg->m, 2); + move_multi_frags(hpp, off_heap, bp, msg->m, +#ifdef USE_VM_PROBES + 3 +#else + 2 +#endif + ); goto copy_done; } @@ -633,6 +737,16 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg) ASSERT(hp > ptr_val(ERL_MESSAGE_TERM(msg))); #endif } +#ifdef USE_VM_PROBES + if (is_not_immed(utag)) { + ASSERT(in_heapfrag(ptr_val(utag), bp)); + ERL_MESSAGE_DT_UTAG(msg) = offset_ptr(utag, offs); +#ifdef HARD_DEBUG + ASSERT(dbg_thp_start <= ptr_val(ERL_MESSAGE_DT_UTAG(msg))); + ASSERT(hp > ptr_val(ERL_MESSAGE_DT_UTAG(msg))); +#endif + } +#endif copy_done: @@ -699,6 +813,9 @@ copy_done: #ifdef HARD_DEBUG ASSERT(eq(ERL_MESSAGE_TERM(msg), dbg_term)); ASSERT(eq(ERL_MESSAGE_TOKEN(msg), dbg_token)); +#ifdef USE_VM_PROBES + ASSERT(eq(ERL_MESSAGE_DT_UTAG(msg), dbg_utag)); +#endif free_message_buffer(dbg_bp); #endif @@ -774,39 +891,101 @@ erts_send_message(Process* sender, Uint msize; ErlHeapFragment* bp = NULL; Eterm token = NIL; - +#ifdef USE_VM_PROBES + DTRACE_CHARBUF(sender_name, 64); + DTRACE_CHARBUF(receiver_name, 64); + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; +#endif BM_STOP_TIMER(system); BM_MESSAGE(message,sender,receiver); BM_START_TIMER(send); + #ifdef USE_VM_PROBES + *sender_name = *receiver_name = '\0'; + if (DTRACE_ENABLED(message_send)) { + erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id); + erts_snprintf(receiver_name, sizeof(receiver_name), "%T", receiver->id); + } +#endif if (SEQ_TRACE_TOKEN(sender) != NIL && !(flags & ERTS_SND_FLG_NO_SEQ_TRACE)) { Eterm* hp; + Eterm stoken = SEQ_TRACE_TOKEN(sender); + Uint seq_trace_size = 0; +#ifdef USE_VM_PROBES + Uint dt_utag_size = 0; + Eterm utag = NIL; +#endif - BM_SWAP_TIMER(send,size); + BM_SWAP_TIMER(send,size); msize = size_object(message); - BM_SWAP_TIMER(size,send); + BM_SWAP_TIMER(size,send); + +#ifdef USE_VM_PROBES + if (stoken != am_have_dt_utag) { +#endif + + seq_trace_update_send(sender); + seq_trace_output(stoken, message, SEQ_TRACE_SEND, + receiver->id, sender); + seq_trace_size = 6; /* TUPLE5 */ +#ifdef USE_VM_PROBES + } + if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) { + dt_utag_size = size_object(DT_UTAG(sender)); + } else if (stoken == am_have_dt_utag ) { + stoken = NIL; + } +#endif - seq_trace_update_send(sender); - seq_trace_output(SEQ_TRACE_TOKEN(sender), message, SEQ_TRACE_SEND, - receiver->id, sender); - bp = new_message_buffer(msize + 6 /* TUPLE5 */); + bp = new_message_buffer(msize + seq_trace_size +#ifdef USE_VM_PROBES + + dt_utag_size +#endif + ); hp = bp->mem; BM_SWAP_TIMER(send,copy); - token = copy_struct(SEQ_TRACE_TOKEN(sender), - 6 /* TUPLE5 */, + token = copy_struct(stoken, + seq_trace_size, &hp, &bp->off_heap); message = copy_struct(message, msize, &hp, &bp->off_heap); +#ifdef USE_VM_PROBES + if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) { + utag = copy_struct(DT_UTAG(sender), dt_utag_size, &hp, &bp->off_heap); +#ifdef DTRACE_TAG_HARDDEBUG + erts_fprintf(stderr, + "Dtrace -> (%T) Spreading tag (%T) with " + "message %T!\r\n",sender->id, utag, message); +#endif + } +#endif BM_MESSAGE_COPIED(msize); BM_SWAP_TIMER(copy,send); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(message_send)) { + if (stoken != NIL && stoken != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(stoken)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(stoken)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(stoken)); + } + DTRACE6(message_send, sender_name, receiver_name, + msize, tok_label, tok_lastcnt, tok_serial); + } +#endif erts_queue_message(receiver, receiver_locks, bp, message, - token); + token +#ifdef USE_VM_PROBES + , utag +#endif + ); BM_SWAP_TIMER(send,system); #ifdef HYBRID } else { @@ -835,8 +1014,13 @@ erts_send_message(Process* sender, #endif LAZY_COPY(sender,message); BM_SWAP_TIMER(copy,send); + DTRACE6(message_send, sender_name, receiver_name, + size_object(message)msize, tok_label, tok_lastcnt, tok_serial); ERL_MESSAGE_TERM(mp) = message; ERL_MESSAGE_TOKEN(mp) = NIL; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = NIL; +#endif mp->next = NULL; LINK_MESSAGE(receiver, mp); ACTIVATE(receiver); @@ -874,9 +1058,14 @@ erts_send_message(Process* sender, { ErlMessage* mp = message_alloc(); + DTRACE6(message_send, sender_name, receiver_name, + size_object(message), tok_label, tok_lastcnt, tok_serial); mp->data.attached = NULL; ERL_MESSAGE_TERM(mp) = message; ERL_MESSAGE_TOKEN(mp) = NIL; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = NIL; +#endif mp->next = NULL; /* * We move 'in queue' to 'private queue' and place @@ -908,7 +1097,13 @@ erts_send_message(Process* sender, message = copy_struct(message, msize, &hp, ohp); BM_MESSAGE_COPIED(msz); BM_SWAP_TIMER(copy,send); - erts_queue_message(receiver, receiver_locks, bp, message, token); + DTRACE6(message_send, sender_name, receiver_name, + msize, tok_label, tok_lastcnt, tok_serial); + erts_queue_message(receiver, receiver_locks, bp, message, token +#ifdef USE_VM_PROBES + , NIL +#endif + ); BM_SWAP_TIMER(send,system); #else ErlMessage* mp = message_alloc(); @@ -928,8 +1123,13 @@ erts_send_message(Process* sender, message = copy_struct(message, msize, &hp, &receiver->off_heap); BM_MESSAGE_COPIED(msize); BM_SWAP_TIMER(copy,send); + DTRACE6(message_send, sender_name, receiver_name, + (uint32_t)msize, tok_label, tok_lastcnt, tok_serial); ERL_MESSAGE_TERM(mp) = message; ERL_MESSAGE_TOKEN(mp) = NIL; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = NIL; +#endif mp->next = NULL; mp->data.attached = NULL; LINK_MESSAGE(receiver, mp); @@ -968,7 +1168,11 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, Eterm temptoken; ErlHeapFragment* bp = NULL; - if (token != NIL) { + if (token != NIL +#ifdef USE_VM_PROBES + && token != am_have_dt_utag +#endif + ) { ASSERT(is_tuple(token)); sz_reason = size_object(reason); @@ -983,7 +1187,11 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, /* the trace token must in this case be updated by the caller */ seq_trace_output(token, save, SEQ_TRACE_SEND, to->id, NULL); temptoken = copy_struct(token, sz_token, &hp, &bp->off_heap); - erts_queue_message(to, to_locksp, bp, save, temptoken); + erts_queue_message(to, to_locksp, bp, save, temptoken +#ifdef USE_VM_PROBES + , NIL +#endif + ); } else { ErlOffHeap *ohp; sz_reason = size_object(reason); @@ -1000,7 +1208,11 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, ? from : copy_struct(from, sz_from, &hp, ohp)); save = TUPLE3(hp, am_EXIT, from_copy, mess); - erts_queue_message(to, to_locksp, bp, save, NIL); + erts_queue_message(to, to_locksp, bp, save, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); } } diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 5aca0db6fe..7678c7c753 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -70,11 +70,18 @@ typedef struct erl_mesg { ErlHeapFragment *heap_frag; void *attached; } data; +#ifdef USE_VM_PROBES + Eterm m[3]; /* m[0] = message, m[1] = seq trace token, m[3] = dynamic trace user tag */ +#else Eterm m[2]; /* m[0] = message, m[1] = seq trace token */ +#endif } ErlMessage; #define ERL_MESSAGE_TERM(mp) ((mp)->m[0]) #define ERL_MESSAGE_TOKEN(mp) ((mp)->m[1]) +#ifdef USE_VM_PROBES +#define ERL_MESSAGE_DT_UTAG(mp) ((mp)->m[2]) +#endif /* Size of default message buffer (erl_message.c) */ #define ERL_MESSAGE_BUF_SZ 500 @@ -221,7 +228,11 @@ ErlHeapFragment* erts_resize_message_buffer(ErlHeapFragment *, Uint, Eterm *, Uint); void free_message_buffer(ErlHeapFragment *); void erts_queue_dist_message(Process*, ErtsProcLocks*, ErtsDistExternal *, Eterm); -void erts_queue_message(Process*, ErtsProcLocks*, ErlHeapFragment*, Eterm, Eterm); +void erts_queue_message(Process*, ErtsProcLocks*, ErlHeapFragment*, Eterm, Eterm +#ifdef USE_VM_PROBES + , Eterm dt_utag +#endif +); void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm); void erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned); void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 58a09986d2..40f2fde578 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -66,6 +66,9 @@ static void add_readonly_check(ErlNifEnv*, unsigned char* ptr, unsigned sz); static int is_offheap(const ErlOffHeap* off_heap); #endif +#ifdef USE_VM_PROBES +void dtrace_nifenv_str(ErlNifEnv *, char *); +#endif #define MIN_HEAP_FRAG_SZ 200 static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp); @@ -350,7 +353,11 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, if (flush_me) { flush_env(env); /* Needed for ERTS_HOLE_CHECK */ } - erts_queue_message(rp, &rp_locks, frags, msg, am_undefined); + erts_queue_message(rp, &rp_locks, frags, msg, am_undefined +#ifdef USE_VM_PROBES + , NIL +#endif + ); if (rp_locks) { ERTS_SMP_LC_ASSERT(rp_locks == (rp_had_locks | (ERTS_PROC_LOCK_MSGQ | ERTS_PROC_LOCK_STATUS))); @@ -1779,6 +1786,13 @@ void erl_nif_init() resource_type_list.name = THE_NON_VALUE; } +#ifdef USE_VM_PROBES +void dtrace_nifenv_str(ErlNifEnv *env, char *process_buf) +{ + dtrace_pid_str(env->proc->id, process_buf); +} +#endif + #ifdef READONLY_CHECK /* Use checksums to assert that NIFs do not write into inspected binaries */ diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 908ba755ed..1481f66b55 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -27,6 +27,7 @@ #include "big.h" #include "error.h" #include "erl_thr_progress.h" +#include "dtrace-wrapper.h" Hash erts_dist_table; Hash erts_node_table; @@ -42,6 +43,8 @@ Sint erts_no_of_not_connected_dist_entries; DistEntry *erts_this_dist_entry; ErlNode *erts_this_node; +char erts_this_node_sysname_BUFFER[256], + *erts_this_node_sysname = "uninitialized yet"; static Uint node_entries; static Uint dist_entries; @@ -702,6 +705,9 @@ erts_set_this_node(Eterm sysname, Uint creation) (void) hash_erase(&erts_node_table, (void *) erts_this_node); erts_this_node->sysname = sysname; erts_this_node->creation = creation; + erts_this_node_sysname = erts_this_node_sysname_BUFFER; + erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname), + "%T", sysname); (void) hash_put(&erts_node_table, (void *) erts_this_node); erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx); @@ -789,6 +795,9 @@ void erts_init_node_tables(void) erts_this_node->sysname = am_Noname; erts_this_node->creation = 0; erts_this_node->dist_entry = erts_this_dist_entry; + erts_this_node_sysname = erts_this_node_sysname_BUFFER; + erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname), + "%T", erts_this_node->sysname); (void) hash_put(&erts_node_table, (void *) erts_this_node); diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index b0a63ae035..5cfd0ac641 100644 --- a/erts/emulator/beam/erl_node_tables.h +++ b/erts/emulator/beam/erl_node_tables.h @@ -169,6 +169,7 @@ extern Sint erts_no_of_not_connected_dist_entries; extern DistEntry *erts_this_dist_entry; extern ErlNode *erts_this_node; +extern char *erts_this_node_sysname; /* must match erl_node_tables.c */ DistEntry *erts_channel_no_to_dist_entry(Uint); DistEntry *erts_sysname_to_connected_dist_entry(Eterm); diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index a2b08fcf56..a8cb4563d6 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -32,6 +32,7 @@ #include "global.h" #include "erl_port_task.h" #include "dist.h" +#include "dtrace-wrapper.h" #if defined(DEBUG) && 0 #define HARD_DEBUG @@ -61,6 +62,20 @@ do { \ (P)->sched.next = NULL; \ } while (0) +#ifdef USE_VM_PROBES +#define DTRACE_DRIVER(PROBE_NAME, PP) \ + if (DTRACE_ENABLED(driver_ready_input)) { \ + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); \ + \ + dtrace_pid_str(PP->connected, process_str); \ + dtrace_port_str(PP, port_str); \ + DTRACE3(PROBE_NAME, process_str, port_str, PP->name); \ + } +#else +#define DTRACE_DRIVER(PROBE_NAME, PP) do {} while(0) +#endif + erts_smp_atomic_t erts_port_task_outstanding_io_tasks; struct ErtsPortTaskQueue_ { @@ -823,12 +838,15 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) goto tasks_done; case ERTS_PORT_TASK_TIMEOUT: reds += ERTS_PORT_REDS_TIMEOUT; - if (!(pp->status & ERTS_PORT_SFLGS_DEAD)) + if (!(pp->status & ERTS_PORT_SFLGS_DEAD)) { + DTRACE_DRIVER(driver_timeout, pp); (*pp->drv_ptr->timeout)((ErlDrvData) pp->drv_data); + } break; case ERTS_PORT_TASK_INPUT: reds += ERTS_PORT_REDS_INPUT; ASSERT((pp->status & ERTS_PORT_SFLGS_DEAD) == 0); + DTRACE_DRIVER(driver_ready_input, pp); /* NOTE some windows drivers use ->ready_input for input and output */ (*pp->drv_ptr->ready_input)((ErlDrvData) pp->drv_data, ptp->event); io_tasks_executed++; @@ -836,12 +854,14 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) case ERTS_PORT_TASK_OUTPUT: reds += ERTS_PORT_REDS_OUTPUT; ASSERT((pp->status & ERTS_PORT_SFLGS_DEAD) == 0); + DTRACE_DRIVER(driver_ready_output, pp); (*pp->drv_ptr->ready_output)((ErlDrvData) pp->drv_data, ptp->event); io_tasks_executed++; break; case ERTS_PORT_TASK_EVENT: reds += ERTS_PORT_REDS_EVENT; ASSERT((pp->status & ERTS_PORT_SFLGS_DEAD) == 0); + DTRACE_DRIVER(driver_event, pp); (*pp->drv_ptr->event)((ErlDrvData) pp->drv_data, ptp->event, ptp->event_data); io_tasks_executed++; break; diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 45f6dc800c..95d408f79d 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -42,6 +42,7 @@ #include "erl_thr_progress.h" #include "erl_thr_queue.h" #include "erl_async.h" +#include "dtrace-wrapper.h" #define ERTS_RUNQ_CHECK_BALANCE_REDS_PER_SCHED (2000*CONTEXT_REDS) #define ERTS_RUNQ_CALL_CHECK_BALANCE_REDS \ @@ -678,7 +679,11 @@ reply_sched_wall_time(void *vswtrp) hpp = &hp; } - erts_queue_message(rp, &rp_locks, bp, msg, NIL); + erts_queue_message(rp, &rp_locks, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); if (swtrp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -6214,6 +6219,15 @@ Process *schedule(Process *p, int calls) int actual_reds; int reds; +#ifdef USE_VM_PROBES + if (p != NULL && DTRACE_ENABLED(process_unscheduled)) { + DTRACE_CHARBUF(process_buf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, process_buf); + DTRACE1(process_unscheduled, process_buf); + } +#endif + if (ERTS_USE_MODIFIED_TIMING()) { context_reds = ERTS_MODIFIED_TIMING_CONTEXT_REDS; input_reductions = ERTS_MODIFIED_TIMING_INPUT_REDS; @@ -7252,6 +7266,10 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->seq_trace_lastcnt = 0; p->seq_trace_clock = 0; SEQ_TRACE_TOKEN(p) = NIL; +#ifdef USE_VM_PROBES + DT_UTAG(p) = NIL; + DT_UTAG_FLAGS(p) = 0; +#endif p->parent = parent->id == ERTS_INVALID_PID ? NIL : parent->id; #ifdef HYBRID @@ -7384,6 +7402,16 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->id)); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_spawn)) { + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); + + dtrace_fun_decode(p, mod, func, arity, process_name, mfa); + DTRACE2(process_spawn, process_name, mfa); + } +#endif + error: erts_smp_proc_unlock(parent, ERTS_PROC_LOCKS_ALL_MINOR); @@ -7835,7 +7863,11 @@ static ERTS_INLINE void send_exit_message(Process *to, ErtsProcLocks *to_locksp, Eterm exit_term, Uint term_size, Eterm token) { - if (token == NIL) { + if (token == NIL +#ifdef USE_VM_PROBES + || token == am_have_dt_utag +#endif + ) { Eterm* hp; Eterm mess; ErlHeapFragment* bp; @@ -7843,7 +7875,11 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, hp = erts_alloc_message_heap(term_size, &bp, &ohp, to, to_locksp); mess = copy_struct(exit_term, term_size, &hp, ohp); - erts_queue_message(to, to_locksp, bp, mess, NIL); + erts_queue_message(to, to_locksp, bp, mess, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); } else { ErlHeapFragment* bp; Eterm* hp; @@ -7859,7 +7895,11 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, /* the trace token must in this case be updated by the caller */ seq_trace_output(token, mess, SEQ_TRACE_SEND, to->id, NULL); temp_token = copy_struct(token, sz_token, &hp, &bp->off_heap); - erts_queue_message(to, to_locksp, bp, mess, temp_token); + erts_queue_message(to, to_locksp, bp, mess, temp_token +#ifdef USE_VM_PROBES + , NIL +#endif + ); } } @@ -7952,9 +7992,26 @@ send_exit_signal(Process *c_p, /* current process if and only ASSERT(reason != THE_NON_VALUE); +#ifdef USE_VM_PROBES + if(DTRACE_ENABLED(process_exit_signal) && is_pid(from)) { + DTRACE_CHARBUF(sender_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(receiver_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(reason_buf, DTRACE_TERM_BUF_SIZE); + + dtrace_pid_str(from, sender_str); + dtrace_proc_str(rp, receiver_str); + erts_snprintf(reason_buf, sizeof(reason_buf) - 1, "%T", reason); + DTRACE3(process_exit_signal, sender_str, receiver_str, reason_buf); + } +#endif + if (ERTS_PROC_IS_TRAPPING_EXITS(rp) && (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) { - if (is_not_nil(token) && token_update) + if (is_not_nil(token) +#ifdef USE_VM_PROBES + && token != am_have_dt_utag +#endif + && token_update) seq_trace_update_send(token_update); if (is_value(exit_tuple)) send_exit_message(rp, rp_locks, exit_tuple, exit_tuple_sz, token); @@ -8378,7 +8435,18 @@ erts_do_exit_process(Process* p, Eterm reason) p->arity = 0; /* No live registers */ p->fvalue = reason; - + +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_exit)) { + DTRACE_CHARBUF(process_buf, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(reason_buf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, process_buf); + erts_snprintf(reason_buf, DTRACE_TERM_BUF_SIZE - 1, "%T", reason); + DTRACE2(process_exit, process_buf, reason_buf); + } +#endif + #ifdef ERTS_SMP ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); /* By locking all locks (main lock is already locked) when going diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index d671638ce8..cff0783bc4 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -683,6 +683,10 @@ struct process { Uint seq_trace_lastcnt; Eterm seq_trace_token; /* Sequential trace token (tuple size 5 see below) */ +#ifdef USE_VM_PROBES + Eterm dt_utag; /* Place to store the dynamc trace user tag */ + Uint dt_utag_flags; /* flag field for the dt_utag */ +#endif BeamInstr initial[3]; /* Initial module(0), function(1), arity(2), often used instead of pointer to funcinfo instruction, hence the BeamInstr datatype */ BeamInstr* current; /* Current Erlang function, part of the funcinfo: @@ -998,6 +1002,14 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags; #define SEQ_TRACE_PRINT (1 << 2) #define SEQ_TRACE_TIMESTAMP (1 << 3) +#ifdef USE_VM_PROBES +#define DT_UTAG_PERMANENT (1 << 0) +#define DT_UTAG_SPREADING (1 << 1) +#define DT_UTAG(P) ((P)->dt_utag) +#define DT_UTAG_FLAGS(P) ((P)->dt_utag_flags) +#endif + + #ifdef ERTS_SMP /* Status flags ... */ #define ERTS_PROC_SFLG_PENDADD2SCHEDQ (((Uint32) 1) << 0) /* Pending diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index b1d1e1d9b0..4261cd03be 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -125,8 +125,13 @@ do { \ enqueue_sys_msg_unlocked(SYS_MSG_TYPE_TRACE, (FPID), (TPID), (MSG), (BP)); \ } while(0) #else +#ifdef USE_VM_PROBES #define ERTS_ENQ_TRACE_MSG(FPID, TPROC, MSG, BP) \ - erts_queue_message((TPROC), NULL, (BP), (MSG), NIL) + erts_queue_message((TPROC), NULL, (BP), (MSG), NIL, NIL) +#else +#define ERTS_ENQ_TRACE_MSG(FPID, TPROC, MSG, BP) \ + erts_queue_message((TPROC), NULL, (BP), (MSG), NIL) +#endif #endif /* @@ -583,7 +588,11 @@ profile_send(Eterm from, Eterm message) { hp = erts_alloc_message_heap(sz, &bp, &off_heap, profile_p, 0); msg = copy_struct(message, sz, &hp, &bp->off_heap); - erts_queue_message(profile_p, NULL, bp, msg, NIL); + erts_queue_message(profile_p, NULL, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); } } @@ -994,9 +1003,13 @@ seq_trace_update_send(Process *p) { Eterm seq_tracer = erts_get_system_seq_tracer(); ASSERT((is_tuple(SEQ_TRACE_TOKEN(p)) || is_nil(SEQ_TRACE_TOKEN(p)))); - if ( (p->id == seq_tracer) || (SEQ_TRACE_TOKEN(p) == NIL)) + if ( (p->id == seq_tracer) || (SEQ_TRACE_TOKEN(p) == NIL) +#ifdef USE_VM_PROBES + || (SEQ_TRACE_TOKEN(p) == am_have_dt_utag) +#endif + ) { return 0; - + } SEQ_TRACE_TOKEN_SENDER(p) = p->id; /* Internal pid */ SEQ_TRACE_TOKEN_SERIAL(p) = make_small(++(p -> seq_trace_clock)); @@ -1178,7 +1191,11 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SEQTRACE, NIL, NIL, mess, bp); erts_smp_mtx_unlock(&smq_mtx); #else - erts_queue_message(tracer, NULL, bp, mess, NIL); /* trace_token must be NIL here */ + erts_queue_message(tracer, NULL, bp, mess, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); /* trace_token must be NIL here */ #endif } } @@ -2469,7 +2486,11 @@ monitor_long_gc(Process *p, Uint time) { #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL); + erts_queue_message(monitor_p, NULL, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); #endif } @@ -2541,7 +2562,11 @@ monitor_large_heap(Process *p) { #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL); + erts_queue_message(monitor_p, NULL, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); #endif } @@ -2571,7 +2596,11 @@ monitor_generic(Process *p, Eterm type, Eterm spec) { #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL); + erts_queue_message(monitor_p, NULL, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); #endif } @@ -3357,7 +3386,11 @@ sys_msg_dispatcher_func(void *unused) } else { queue_proc_msg: - erts_queue_message(proc,&proc_locks,smqp->bp,smqp->msg,NIL); + erts_queue_message(proc,&proc_locks,smqp->bp,smqp->msg,NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); #ifdef DEBUG_PRINTOUTS erts_fprintf(stderr, "delivered\n"); #endif diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d new file mode 100644 index 0000000000..587e51cb67 --- /dev/null +++ b/erts/emulator/beam/erlang_dtrace.d @@ -0,0 +1,726 @@ +/* + * %CopyrightBegin% + * + * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 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 + * 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% + */ + +/* + * A note on probe naming: if "__" appears in a provider probe + * definition, then two things happen during compilation: + * + * 1. The "__" will turn into a hypen, "-", for the probe name. + * 2. The "__" will turn into a single underscore, "_", for the + * macro names and function definitions that the compiler and + * C developers will see. + * + * We'll try to use the following naming convention. We're a bit + * limited because, as a USDT probe, we can only specify the 4th part + * of the probe name, e.g. erlang*:::mumble. The 2nd part of the + * probe name is always going to be "beam" or "beam.smp", and the 3rd + * part of the probe name will always be the name of the function + * that's calling the probe. + * + * So, all probes will be have names defined in this file using the + * convention category__name or category__sub_category__name. This + * will translate to probe names of category-name or + * category-sub_category-name. + * + * Each of "category", "sub_category", and "name" may have underscores + * but may not have hyphens. + */ + +provider erlang { + /** + * Fired when a message is sent from one local process to another. + * + * NOTE: The 'size' parameter is in machine-dependent words and + * that the actual size of any binary terms in the message + * are not included. + * + * @param sender the PID (string form) of the sender + * @param receiver the PID (string form) of the receiver + * @param size the size of the message being delivered (words) + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe message__send(char *sender, char *receiver, uint32_t size, + int token_label, int token_previous, int token_current); + + /** + * Fired when a message is sent from a local process to a remote process. + * + * NOTE: The 'size' parameter is in machine-dependent words and + * that the actual size of any binary terms in the message + * are not included. + * + * @param sender the PID (string form) of the sender + * @param node_name the Erlang node name (string form) of the receiver + * @param receiver the PID/name (string form) of the receiver + * @param size the size of the message being delivered (words) + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe message__send__remote(char *sender, char *node_name, char *receiver, + uint32_t size, + int token_label, int token_previous, int token_current); + + /** + * Fired when a message is queued to a local process. This probe + * will not fire if the sender's pid == receiver's pid. + * + * NOTE: The 'size' parameter is in machine-dependent words and + * that the actual size of any binary terms in the message + * are not included. + * + * NOTE: In cases of messages in external format (i.e. from another + * Erlang node), we probably don't know the message size + * without performing substantial extra computation. To + * avoid the extra CPU overhead, the message size may be + * reported as -1, which can appear to a D script as 4294967295. + * + * @param receiver the PID (string form) of the receiver + * @param size the size of the message being delivered (words) + * @param queue_len length of the queue of the receiving process + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe message__queued(char *receiver, uint32_t size, uint32_t queue_len, + int token_label, int token_previous, int token_current); + + /** + * Fired when a message is 'receive'd by a local process and removed + * from its mailbox. + * + * NOTE: The 'size' parameter is in machine-dependent words and + * that the actual size of any binary terms in the message + * are not included. + * + * NOTE: In cases of messages in external format (i.e. from another + * Erlang node), we probably don't know the message size + * without performing substantial extra computation. To + * avoid the extra CPU overhead, the message size may be + * reported as -1, which can appear to a D script as 4294967295. + * + * @param receiver the PID (string form) of the receiver + * @param size the size of the message being delivered (words) + * @param queue_len length of the queue of the receiving process + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe message__receive(char *receiver, uint32_t size, uint32_t queue_len, + int token_label, int token_previous, int token_current); + + /** + * Fired when an Eterm structure is being copied. + * + * NOTE: Due to the placement of this probe, the process ID of + * owner of the Eterm is not available. + * + * @param size the size of the structure + */ + probe copy__struct(uint32_t size); + + /** + * Fired when an Eterm is being copied onto a process. + * + * @param proc the PID (string form) of the recipient process + * @param size the size of the structure + */ + probe copy__object(char *proc, uint32_t size); + + /* PID, Module, Function, Arity */ + + /** + * Fired whenever a user function is being called locally. + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + * @param depth the stack depth + */ + probe local__function__entry(char *p, char *mfa, int depth); + + /** + * Fired whenever a user function is called externally + * (through an export entry). + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + * @param depth the stack depth + */ + probe global__function__entry(char *p, char *mfa, int depth); + + /** + * Fired whenever a user function returns. + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + * @param depth the stack depth + */ + probe function__return(char *p, char *mfa, int depth); + + /** + * Fired whenever a Built In Function is called. + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + */ + probe bif__entry(char *p, char *mfa); + + /** + * Fired whenever a Built In Function returns. + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + */ + probe bif__return(char *p, char *mfa); + + /** + * Fired whenever a Native Function is called. + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + */ + probe nif__entry(char *p, char *mfa); + + /** + * Fired whenever a Native Function returns. + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + */ + probe nif__return(char *p, char *mfa); + + /** + * Fired when a major GC is starting. + * + * @param p the PID (string form) of the exiting process + * @param need the number of words needed on the heap + */ + probe gc_major__start(char *p, int need); + + /** + * Fired when a minor GC is starting. + * + * @param p the PID (string form) of the exiting process + * @param need the number of words needed on the heap + */ + probe gc_minor__start(char *p, int need); + + /** + * Fired when a major GC is starting. + * + * @param p the PID (string form) of the exiting process + * @param reclaimed the amount of space reclaimed + */ + probe gc_major__end(char *p, int reclaimed); + + /** + * Fired when a minor GC is starting. + * + * @param p the PID (string form) of the exiting process + * @param reclaimed the amount of space reclaimed + */ + probe gc_minor__end(char *p, int reclaimed); + + /** + * Fired when a process is spawned. + * + * @param p the PID (string form) of the new process. + * @param mfa the m:f/a of the function + */ + probe process__spawn(char *p, char *mfa); + + /** + * Fired when a process is exiting. + * + * @param p the PID (string form) of the exiting process + * @param reason the reason for the exit (may be truncated) + */ + probe process__exit(char *p, char *reason); + + /** + * Fired when exit signal is delivered to a local process. + * + * @param sender the PID (string form) of the exiting process + * @param receiver the PID (string form) of the process receiving EXIT signal + * @param reason the reason for the exit (may be truncated) + */ + probe process__exit_signal(char *sender, char *receiver, char *reason); + + /** + * Fired when exit signal is delivered to a remote process. + * + * @param sender the PID (string form) of the exiting process + * @param node_name the Erlang node name (string form) of the receiver + * @param receiver the PID (string form) of the process receiving EXIT signal + * @param reason the reason for the exit (may be truncated) + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe process__exit_signal__remote(char *sender, char *node_name, + char *receiver, char *reason, + int token_label, int token_previous, int token_current); + + /** + * Fired when a process is scheduled. + * + * @param p the PID (string form) of the newly scheduled process + * @param mfa the m:f/a of the function it should run next + */ + probe process__scheduled(char *p, char *mfa); + + /** + * Fired when a process is unscheduled. + * + * @param p the PID (string form) of the process that has been + * unscheduled. + */ + probe process__unscheduled(char *p); + + /** + * Fired when a process goes into hibernation. + * + * @param p the PID (string form) of the process entering hibernation + * @param mfa the m:f/a of the location to resume + */ + probe process__hibernate(char *p, char *mfa); + + /** + * Fired when a process is unblocked after a port has been unblocked. + * + * @param p the PID (string form) of the process that has been + * unscheduled. + * @param port the port that is no longer busy (i.e., is now unblocked) + */ + probe process__port_unblocked(char *p, char *port); + + /** + * Fired when process' heap is growing. + * + * @param p the PID (string form) + * @param old_size the size of the old heap + * @param new_size the size of the new heap + */ + probe process__heap_grow(char *p, int old_size, int new_size); + + /** + * Fired when process' heap is shrinking. + * + * @param p the PID (string form) + * @param old_size the size of the old heap + * @param new_size the size of the new heap + */ + probe process__heap_shrink(char *p, int old_size, int new_size); + + /* network distribution */ + + /** + * Fired when network distribution event monitor events are triggered. + * + * @param node the name of the reporting node + * @param what the type of event, e.g., nodeup, nodedown + * @param monitored_node the name of the monitored node + * @param type the type of node, e.g., visible, hidden + * @param reason the reason term, e.g., normal, connection_closed, term() + */ + probe dist__monitor(char *node, char *what, char *monitored_node, + char *type, char *reason); + + /** + * Fired when network distribution port is busy (i.e. blocked), + * usually due to the remote node not consuming distribution + * data quickly enough. + * + * @param node the name of the reporting node + * @param port the port ID of the busy port + * @param remote_node the name of the remote node. + * @param pid the PID (string form) of the local process that has + * become unschedulable until the port becomes unblocked. + */ + probe dist__port_busy(char *node, char *port, char *remote_node, + char *pid); + + /** + * Fired when network distribution's driver's "output" callback is called + * + * @param node the name of the reporting node + * @param port the port ID of the busy port + * @param remote_node the name of the remote node. + * @param bytes the number of bytes written + */ + probe dist__output(char *node, char *port, char *remote_node, int bytes); + + /** + * Fired when network distribution's driver's "outputv" callback is called + * + * @param node the name of the reporting node + * @param port the port ID of the busy port + * @param remote_node the name of the remote node. + * @param bytes the number of bytes written + */ + probe dist__outputv(char *node, char *port, char *remote_node, int bytes); + + /** + * Fired when network distribution port is no longer busy (i.e. blocked). + * + * NOTE: This probe may fire multiple times after the same single + * dist-port_busy probe firing. + * + * @param node the name of the reporting node + * @param port the port ID of the busy port + * @param remote_node the name of the remote node. + */ + probe dist__port_not_busy(char *node, char *port, char *remote_node); + + /* ports */ + + /** + * Fired when new port is opened. + * + * @param process the PID (string form) + * @param port_name the string used when the port was opened + * @param port the Port (string form) of the new port + */ + probe port__open(char *process, char *port_name, char *port); + + /** + * Fired when port_command is issued. + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param command_type type of the issued command, one of: "close", "command" or "connect" + */ + probe port__command(char *process, char *port, char *port_name, char *command_type); + + /** + * Fired when port_control is issued. + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param command_no command number that has been issued to the port + */ + probe port__control(char *process, char *port, char *port_name, int command_no); + + /** + * Fired when port is closed via port_close/1 (reason = 'normal') + * or is sent an exit signal. + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param reason Erlang term representing the exit signal, e.g. 'normal' + */ + probe port__exit(char *process, char *port, char *port_name, + char *new_process); + + /** + * Fired when port_connect is issued. + * + * @param process the PID (string form) of the current port owner + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param new_process the PID (string form) of the new port owner + */ + probe port__connect(char *process, char *port, char *port_name, + char *new_process); + + /** + * Fired when a port is busy (i.e. blocked) + * + * @param port the port ID of the busy port + */ + probe port__busy(char *port); + + /** + * Fired when a port is no longer busy (i.e. no longer blocked) + * + * @param port the port ID of the not busy port + */ + probe port__not_busy(char *port); + + /* drivers */ + + /** + * Fired when drivers's "init" callback is called. + * + * @param name the name of the driver + * @param major the major version number + * @param minor the minor version number + * @param flags the flags argument + */ + probe driver__init(char *name, int major, int minor, int flags); + + /** + * Fired when drivers's "start" callback is called. + * + * @param process the PID (string form) of the calling process + * @param name the name of the driver + * @param port the Port (string form) of the driver's port + */ + probe driver__start(char *process, char *name, char *port); + + /** + * Fired when drivers's "stop" callback is called. + * + * @param process the PID (string form) of the calling process + * @param name the name of the driver + * @param port the Port (string form) of the driver's port + */ + probe driver__stop(char *process, char *name, char *port); + + /** + * Fired when drivers's "finish" callback is called. + * + * @param name the name of the driver + */ + probe driver__finish(char *name); + + /** + * Fired when drivers's "flush" callback is called. + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__flush(char *process, char *port, char *port_name); + + /** + * Fired when driver's "output" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param bytes the number of bytes written + */ + probe driver__output(char *node, char *port, char *port_name, int bytes); + + /** + * Fired when driver's "outputv" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param bytes the number of bytes written + */ + probe driver__outputv(char *node, char *port, char *port_name, int bytes); + + /** + * Fired when driver's "control" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param command the command # + * @param bytes the number of bytes written + */ + probe driver__control(char *node, char *port, char *port_name, + int command, int bytes); + + /** + * Fired when driver's "call" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param command the command # + * @param bytes the number of bytes written + */ + probe driver__call(char *node, char *port, char *port_name, + int command, int bytes); + + /** + * Fired when driver's "event" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__event(char *node, char *port, char *port_name); + + /** + * Fired when driver's "ready_input" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__ready_input(char *node, char *port, char *port_name); + + /** + * Fired when driver's "read_output" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__ready_output(char *node, char *port, char *port_name); + + /** + * Fired when driver's "timeout" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__timeout(char *node, char *port, char *port_name); + + /** + * Fired when drivers's "ready_async" callback is called. + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__ready_async(char *process, char *port, char *port_name); + + /** + * Fired when driver's "process_exit" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__process_exit(char *node, char *port, char *port_name); + + /** + * Fired when driver's "stop_select" callback is called + * + * @param name the name of the driver + */ + probe driver__stop_select(char *name); + + + /* Async driver pool */ + + /** + * Show the post-add length of the async driver thread pool member's queue. + * + * NOTE: The port name is not available: additional lock(s) must + * be acquired in order to get the port name safely in an SMP + * environment. The same is true for the aio__pool_get probe. + * + * @param port the Port (string form) + * @param new queue length + */ + probe aio_pool__add(char *, int); + + /** + * Show the post-get length of the async driver thread pool member's queue. + * + * @param port the Port (string form) + * @param new queue length + */ + probe aio_pool__get(char *, int); + + /* Probes for efile_drv.c */ + + /** + * Entry into the efile_drv.c file I/O driver + * + * For a list of command numbers used by this driver, see the section + * "Guide to probe arguments" in ../../../README.md. That section + * also contains explanation of the various integer and string + * arguments that may be present when any particular probe fires. + * + * NOTE: Not all Linux platforms (using SystemTap) can support + * arguments beyond arg9. + * + * + * TODO: Adding the port string, args[10], is a pain. Making that + * port string available to all the other efile_drv.c probes + * will be more pain. Is the pain worth it? If yes, then + * add them everywhere else and grit our teeth. If no, then + * rip it out. + * + * @param thread-id number of the scheduler Pthread arg0 + * @param tag number: {thread-id, tag} uniquely names a driver operation + * @param user-tag string arg2 + * @param command number arg3 + * @param string argument 1 arg4 + * @param string argument 2 arg5 + * @param integer argument 1 arg6 + * @param integer argument 2 arg7 + * @param integer argument 3 arg8 + * @param integer argument 4 arg9 + * @param port the port ID of the busy port args[10] + */ + probe efile_drv__entry(int, int, char *, int, char *, char *, + int64_t, int64_t, int64_t, int64_t, char *); + + /** + * Entry into the driver's internal work function. Computation here + * is performed by a async worker pool Pthread. + * + * @param thread-id number + * @param tag number + * @param command number + */ + probe efile_drv__int_entry(int, int, int); + + /** + * Return from the driver's internal work function. + * + * @param thread-id number + * @param tag number + * @param command number + */ + probe efile_drv__int_return(int, int, int); + + /** + * Return from the efile_drv.c file I/O driver + * + * @param thread-id number arg0 + * @param tag number arg1 + * @param user-tag string arg2 + * @param command number arg3 + * @param Success? 1 is success, 0 is failure arg4 + * @param If failure, the errno of the error. arg5 + */ + probe efile_drv__return(int, int, char *, int, int, int); + +/* + * NOTE: + * For formatting int64_t arguments within a D script, see: + * + * http://mail.opensolaris.org/pipermail/dtrace-discuss/2006-November/002830.html + * Summary: + * "1) you don't need the 'l' printf() modifiers with DTrace ever" + */ + +/* + * NOTE: For file_drv_return + SMP + R14B03 (and perhaps other + * releases), the sched-thread-id will be the same as the + * work-thread-id: erl_async.c's async_main() function + * will call the asynchronous invoke function and then + * immediately call the drivers ready_async function while + * inside the same I/O worker pool thread. + * For R14B03's source, see erl_async.c lines 302-317. + */ +}; + +#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/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 97d7f0e904..b000e2c5d4 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1974,4 +1974,46 @@ erts_alloc_message_heap(Uint size, # define UseTmpHeapNoproc(Size) /* Nothing */ # define UnUseTmpHeapNoproc(Size) /* Nothing */ #endif /* HEAP_ON_C_STACK */ + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +#include "dtrace-wrapper.h" + +ERTS_GLB_INLINE void +dtrace_pid_str(Eterm pid, char *process_buf) +{ + erts_snprintf(process_buf, DTRACE_TERM_BUF_SIZE, "<%lu.%lu.%lu>", + pid_channel_no(pid), + pid_number(pid), + pid_serial(pid)); +} + +ERTS_GLB_INLINE void +dtrace_proc_str(Process *process, char *process_buf) +{ + dtrace_pid_str(process->id, process_buf); +} + +ERTS_GLB_INLINE void +dtrace_port_str(Port *port, char *port_buf) +{ + erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>", + port_channel_no(port->id), + port_number(port->id)); +} + +ERTS_GLB_INLINE void +dtrace_fun_decode(Process *process, + Eterm module, Eterm function, int arity, + char *process_buf, char *mfa_buf) +{ + if (process_buf) { + dtrace_proc_str(process, process_buf); + } + + erts_snprintf(mfa_buf, DTRACE_TERM_BUF_SIZE, "%T:%T/%d", + module, function, arity); +} +#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ + #endif /* !__GLOBAL_H__ */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index fb1514a147..8a2a43bebd 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -43,6 +43,7 @@ #include "erl_version.h" #include "error.h" #include "erl_async.h" +#include "dtrace-wrapper.h" extern ErlDrvEntry fd_driver_entry; extern ErlDrvEntry vanilla_driver_entry; @@ -180,6 +181,20 @@ typedef struct line_buf_context { #define LINEBUF_INITIAL 100 +#ifdef USE_VM_PROBES +#define DTRACE_FORMAT_COMMON_PID_AND_PORT(PID, PORT) \ + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); \ + \ + dtrace_pid_str((PID), process_str); \ + dtrace_port_str((PORT), port_str); +#define DTRACE_FORMAT_COMMON_PROC_AND_PORT(PID, PORT) \ + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); \ + \ + dtrace_proc_str((PID), process_str); \ + dtrace_port_str((PORT), port_str); +#endif /* The 'number' field in a port now has two parts: the lowest bits contain the index in the port table, and the higher bits are a counter @@ -639,6 +654,12 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ trace_sched_ports_where(port, am_in, am_start); } port->caller = pid; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_start)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(pid, port) + DTRACE3(driver_start, process_str, driver->name, port_str); + } +#endif fpe_was_unmasked = erts_block_fpe(); drv_data = (*driver->start)((ErlDrvPort)(port_ix), name, opts); @@ -1170,6 +1191,12 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) ev.size = size; /* total size */ ev.iov = ivp; ev.binv = bvp; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_outputv)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p) + DTRACE4(driver_outputv, process_str, port_str, p->name, size); + } +#endif fpe_was_unmasked = erts_block_fpe(); (*drv->outputv)((ErlDrvData)p->drv_data, &ev); erts_unblock_fpe(fpe_was_unmasked); @@ -1189,8 +1216,21 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) buf = erts_alloc(ERTS_ALC_T_TMP, size+1); r = io_list_to_buf(list, buf, size); +#ifdef USE_VM_PROBES + if(DTRACE_ENABLED(port_command)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p) + DTRACE4(port_command, process_str, port_str, p->name, "command"); + } +#endif + if (r >= 0) { size -= r; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_output)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p) + DTRACE4(driver_output, process_str, port_str, p->name, size); + } +#endif fpe_was_unmasked = erts_block_fpe(); (*drv->output)((ErlDrvData)p->drv_data, buf, size); erts_unblock_fpe(fpe_was_unmasked); @@ -1214,6 +1254,12 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) */ buf = erts_alloc(ERTS_ALC_T_TMP, size+1); r = io_list_to_buf(list, buf, size); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_output)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p) + DTRACE4(driver_output, process_str, port_str, p->name, size); + } +#endif fpe_was_unmasked = erts_block_fpe(); (*drv->output)((ErlDrvData)p->drv_data, buf, size); erts_unblock_fpe(fpe_was_unmasked); @@ -1529,7 +1575,11 @@ deliver_result(Eterm sender, Eterm pid, Eterm res) hp = erts_alloc_message_heap(sz_res + 3, &bp, &ohp, rp, &rp_locks); res = copy_struct(res, sz_res, &hp, ohp); tuple = TUPLE2(hp, sender, res); - erts_queue_message(rp, &rp_locks, bp, tuple, NIL); + erts_queue_message(rp, &rp_locks, bp, tuple, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); erts_smp_proc_unlock(rp, rp_locks); erts_smp_proc_dec_refc(rp); } @@ -1618,7 +1668,11 @@ static void deliver_read_message(Port* prt, Eterm to, tuple = TUPLE2(hp, prt->id, tuple); hp += 3; - erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined); + erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined +#ifdef USE_VM_PROBES + , NIL +#endif + ); erts_smp_proc_unlock(rp, rp_locks); erts_smp_proc_dec_refc(rp); } @@ -1771,7 +1825,11 @@ deliver_vec_message(Port* prt, /* Port */ tuple = TUPLE2(hp, prt->id, tuple); hp += 3; - erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined); + erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined +#ifdef USE_VM_PROBES + , NIL +#endif + ); erts_smp_proc_unlock(rp, rp_locks); erts_smp_proc_dec_refc(rp); } @@ -1810,6 +1868,12 @@ static void flush_port(Port *p) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); if (p->drv_ptr->flush != NULL) { +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_flush)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(p->connected, p) + DTRACE3(driver_flush, process_str, port_str, p->name); + } +#endif if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) { trace_sched_ports_where(p, am_in, am_flush); } @@ -1865,6 +1929,12 @@ terminate_port(Port *prt) drv = prt->drv_ptr; if ((drv != NULL) && (drv->stop != NULL)) { int fpe_was_unmasked = erts_block_fpe(); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_stop)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(prt->connected, prt) + DTRACE3(driver_stop, process_str, drv->name, port_str); + } +#endif (*drv->stop)((ErlDrvData)prt->drv_data); erts_unblock_fpe(fpe_was_unmasked); #ifdef ERTS_SMP @@ -2026,6 +2096,19 @@ erts_do_exit_port(Port *p, Eterm from, Eterm reason) rreason = (reason == am_kill) ? am_killed : reason; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(port_exit)) { + DTRACE_CHARBUF(from_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(rreason_str, 64); + + erts_snprintf(from_str, sizeof(from_str), "%T", from); + dtrace_port_str(p, port_str); + erts_snprintf(rreason_str, sizeof(rreason_str), "%T", rreason); + DTRACE4(port_exit, from_str, port_str, p->name, rreason_str); + } +#endif + if ((p->status & (ERTS_PORT_SFLGS_DEAD | ERTS_PORT_SFLG_EXITING | ERTS_PORT_SFLG_IMMORTAL)) @@ -2126,6 +2209,13 @@ void erts_port_command(Process *proc, if (tp[2] == am_close) { erts_port_status_bor_set(port, ERTS_PORT_SFLG_SEND_CLOSED); erts_do_exit_port(port, pid, am_normal); + +#ifdef USE_VM_PROBES + if(DTRACE_ENABLED(port_command)) { + DTRACE_FORMAT_COMMON_PROC_AND_PORT(proc, port) + DTRACE4(port_command, process_str, port_str, port->name, "close"); + } +#endif goto done; } else if (is_tuple_arity(tp[2], 2)) { tp = tuple_val(tp[2]); @@ -2133,6 +2223,12 @@ void erts_port_command(Process *proc, if (erts_write_to_port(caller_id, port, tp[2]) == 0) goto done; } else if ((tp[1] == am_connect) && is_internal_pid(tp[2])) { +#ifdef USE_VM_PROBES + if(DTRACE_ENABLED(port_command)) { + DTRACE_FORMAT_COMMON_PROC_AND_PORT(proc, port) + DTRACE4(port_command, process_str, port_str, port->name, "connect"); + } +#endif port->connected = tp[2]; deliver_result(port->id, pid, am_connected); goto done; @@ -2235,6 +2331,15 @@ erts_port_control(Process* p, Port* prt, Uint command, Eterm iolist) erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN); ERTS_SMP_CHK_NO_PROC_LOCKS; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(port_control) || DTRACE_ENABLED(driver_control)) { + DTRACE_FORMAT_COMMON_PROC_AND_PORT(p, prt); + DTRACE4(port_control, process_str, port_str, prt->name, command); + DTRACE5(driver_control, process_str, port_str, prt->name, + command, to_len); + } +#endif + /* * Call the port's control routine. */ @@ -2375,6 +2480,10 @@ print_port_info(int to, void *arg, int i) void set_busy_port(ErlDrvPort port_num, int on) { +#ifdef USE_VM_PROBES + DTRACE_CHARBUF(port_str, 16); +#endif + ERTS_SMP_CHK_NO_PROC_LOCKS; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(&erts_port[port_num])); @@ -2382,12 +2491,26 @@ set_busy_port(ErlDrvPort port_num, int on) if (on) { erts_port_status_bor_set(&erts_port[port_num], ERTS_PORT_SFLG_PORT_BUSY); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(port_busy)) { + erts_snprintf(port_str, sizeof(port_str), + "%T", erts_port[port_num].id); + DTRACE1(port_busy, port_str); + } +#endif } else { ErtsProcList* plp = erts_port[port_num].suspended; erts_port_status_band_set(&erts_port[port_num], ~ERTS_PORT_SFLG_PORT_BUSY); erts_port[port_num].suspended = NULL; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(port_not_busy)) { + erts_snprintf(port_str, sizeof(port_str), + "%T", erts_port[port_num].id); + DTRACE1(port_not_busy, port_str); + } +#endif if (erts_port[port_num].dist_entry) { /* * Processes suspended on distribution ports are @@ -2405,6 +2528,28 @@ set_busy_port(ErlDrvPort port_num, int on) */ if (plp) { +#ifdef USE_VM_PROBES + /* + * Hrm, for blocked dist ports, plp always seems to be NULL. + * That's not so fun. + * Well, another way to get the same info is using a D + * script to correlate an earlier process-port_blocked+pid + * event with a later process-scheduled event. That's + * subject to the multi-CPU races with how events are + * handled, but hey, that way works most of the time. + */ + if (DTRACE_ENABLED(process_port_unblocked)) { + DTRACE_CHARBUF(pid_str, 16); + ErtsProcList* plp2 = plp; + + erts_snprintf(port_str, sizeof(port_str), + "%T", erts_port[port_num]); + while (plp2 != NULL) { + erts_snprintf(pid_str, sizeof(pid_str), "%T", plp2->pid); + DTRACE2(process_port_unblocked, pid_str, port_str); + } + } +#endif /* First proc should be resumed last */ if (plp->next) { erts_resume_processes(plp->next); @@ -2451,6 +2596,14 @@ void erts_raw_port_command(Port* p, byte* buf, Uint len) p->drv_ptr->name ? p->drv_ptr->name : "unknown"); p->caller = NIL; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_output)) { + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); + + dtrace_port_str(p, port_str); + DTRACE4(driver_output, "-raw-", port_str, p->name, len); + } +#endif fpe_was_unmasked = erts_block_fpe(); (*p->drv_ptr->output)((ErlDrvData)p->drv_data, (char*) buf, (int) len); erts_unblock_fpe(fpe_was_unmasked); @@ -2466,6 +2619,12 @@ int async_ready(Port *p, void* data) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); ASSERT(!(p->status & ERTS_PORT_SFLGS_DEAD)); if (p->drv_ptr->ready_async != NULL) { +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_ready_async)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(p->connected, p) + DTRACE3(driver_ready_async, process_str, port_str, p->name); + } +#endif (*p->drv_ptr->ready_async)((ErlDrvData)p->drv_data, data); need_free = 0; #ifdef ERTS_SMP @@ -2660,7 +2819,11 @@ void driver_report_exit(int ix, int status) hp += 3; tuple = TUPLE2(hp, prt->id, tuple); - erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined); + erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined +#ifdef USE_VM_PROBES + , NIL +#endif + ); erts_smp_proc_unlock(rp, rp_locks); erts_smp_proc_dec_refc(rp); @@ -3210,7 +3373,11 @@ driver_deliver_term(ErlDrvPort port, HRelease(rp, hp_end, hp); } /* send message */ - erts_queue_message(rp, &rp_locks, bp, mess, am_undefined); + erts_queue_message(rp, &rp_locks, bp, mess, am_undefined +#ifdef USE_VM_PROBES + , NIL +#endif + ); } else { if (b2t.ix > b2t.used) @@ -4441,6 +4608,12 @@ void erts_fire_port_monitor(Port *prt, Eterm ref) ASSERT(callback != NULL); ref_to_driver_monitor(ref,&drv_monitor); DRV_MONITOR_UNLOCK_PDL(prt); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_process_exit)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(prt->connected, prt) + DTRACE3(driver_process_exit, process_str, port_str, prt->name); + } +#endif fpe_was_unmasked = erts_block_fpe(); (*callback)((ErlDrvData) (prt->drv_data), &drv_monitor); erts_unblock_fpe(fpe_was_unmasked); @@ -4884,6 +5057,8 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle) else { int res; int fpe_was_unmasked = erts_block_fpe(); + DTRACE4(driver_init, drv->name, drv->version.major, drv->version.minor, + drv->flags); res = (*de->init)(); erts_unblock_fpe(fpe_was_unmasked); return res; diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index fc53a88a3a..b2fc571032 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -883,6 +883,95 @@ call_ext_last u==3 u$func:erlang:hibernate/3 D => i_hibernate call_ext_only u==3 u$func:erlang:hibernate/3 => i_hibernate # +# If VM probes are not enabled, we want to short-circult calls to +# the dt tag BIFs to make them as cheap as possible. +# + +%unless USE_VM_PROBES + +call_ext Arity u$func:erlang:dt_get_tag/0 => \ + move a=am_undefined r +call_ext_last Arity u$func:erlang:dt_get_tag/0 D => \ + move a=am_undefined r | deallocate D | return +call_ext_only Arity u$func:erlang:dt_get_tag/0 => \ + move a=am_undefined r | return + +move Any r | call_ext Arity u$func:erlang:dt_put_tag/1 => \ + move a=am_undefined r +move Any r | call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \ + move a=am_undefined r | deallocate D | return +move Any r | call_ext_only Arity u$func:erlang:dt_put_tag/1 => \ + move a=am_undefined r | return +call_ext Arity u$func:erlang:dt_put_tag/1 => \ + move a=am_undefined r +call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \ + move a=am_undefined r | deallocate D | return +call_ext_only Arity u$func:erlang:dt_put_tag/1 => \ + move a=am_undefined r | return + +call_ext Arity u$func:erlang:dt_get_tag_data/0 => \ + move a=am_undefined r +call_ext_last Arity u$func:erlang:dt_get_tag_data/0 D => \ + move a=am_undefined r | deallocate D | return +call_ext_only Arity u$func:erlang:dt_get_tag_data/0 => \ + move a=am_undefined r | return + +move Any r | call_ext Arity u$func:erlang:dt_spread_tag/1 => \ + move a=am_true r +move Any r | call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \ + move a=am_true r | deallocate D | return +move Any r | call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \ + move a=am_true r | return +call_ext Arity u$func:erlang:dt_spread_tag/1 => \ + move a=am_true r +call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \ + move a=am_true r | deallocate D | return +call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \ + move a=am_true r | return + +move Any r | call_ext Arity u$func:erlang:dt_restore_tag/1 => \ + move a=am_true r +move Any r | call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \ + move a=am_true r | deallocate D | return +move Any r | call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \ + move a=am_true r | return +call_ext Arity u$func:erlang:dt_restore_tag/1 => \ + move a=am_true r +call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \ + move a=am_true r | deallocate D | return +call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \ + move a=am_true r | return + +move Any r | call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \ + move Any r +move Any r | call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \ + move Any r | deallocate D | return +move Any r | call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \ + move Any r | return +call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 => +call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \ + deallocate D | return +call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \ + return + +move Any r | call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 => \ + move Any r +move Any r | call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \ + move Any r | deallocate D | return +move Any r | call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \ + move Any r | return +call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 => +call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \ + deallocate D | return +call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \ + return + +# Can happen after one of the transformations above. +move Discarded r | move Something r => move Something r + +%endif + +# # The general case for BIFs that have no special instructions. # A BIF used in the tail must be followed by a return instruction. # diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 49b6618f73..5ab51fab50 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1697,7 +1697,11 @@ static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, int len) erts_queue_error_logger_message(from, tuple3, bp); } #else - erts_queue_message(p, NULL /* only used for smp build */, bp, tuple3, NIL); + erts_queue_message(p, NULL /* only used for smp build */, bp, tuple3, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); #endif return 0; } diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index a251b064da..b33a8d210b 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -100,7 +100,7 @@ #endif #include <stdlib.h> -// Need (NON)BLOCKING macros for sendfile +/* Need (NON)BLOCKING macros for sendfile */ #ifndef WANT_NONBLOCKING #define WANT_NONBLOCKING #endif @@ -112,6 +112,7 @@ #include "erl_threads.h" #include "zlib.h" #include "gzio.h" +#include "dtrace-wrapper.h" #include <ctype.h> #include <sys/types.h> @@ -119,6 +120,39 @@ void erl_exit(int n, char *fmt, ...); static ErlDrvSysInfo sys_info; +/* For explanation of this var, see comment for same var in erl_async.c */ +static unsigned gcc_optimizer_hack = 0; + +#ifdef USE_VM_PROBES + +#define DTRACE_EFILE_BUFSIZ 128 + +#define DTRACE_INVOKE_SETUP(op) \ + do { DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2, op); } while (0) +#define DTRACE_INVOKE_SETUP_BY_NAME(op) \ + struct t_data *d = (struct t_data *) data ; \ + DTRACE_INVOKE_SETUP(op) +#define DTRACE_INVOKE_RETURN(op) \ + do { DTRACE3(efile_drv_int_return, d->sched_i1, d->sched_i2, \ + op); } while (0) ; gcc_optimizer_hack++ ; + +/* Assign human-friendlier id numbers to scheduler & I/O worker threads */ +int dt_driver_idnum = 0; +int dt_driver_io_worker_base = 5000; +erts_mtx_t dt_driver_mutex; +pthread_key_t dt_driver_key; + +typedef struct { + int thread_num; + Uint64 tag; +} dt_private; + +dt_private *get_dt_private(int); +#else /* USE_VM_PROBES */ +#define DTRACE_INVOKE_SETUP(op) do {} while (0) +#define DTRACE_INVOKE_SETUP_BY_NAME(op) do {} while (0) +#define DTRACE_INVOKE_RETURN(op) do {} while (0) +#endif /* USE_VM_PROBES */ /* #define TRACE 1 */ #ifdef TRACE @@ -174,6 +208,9 @@ static ErlDrvSysInfo sys_info; #ifdef FILENAMES_16BIT +#ifdef USE_VM_PROBES +#error 16bit characters in filenames and dtrace in combination is not supported. +#endif # define FILENAME_BYTELEN(Str) filename_len_16bit(Str) # define FILENAME_COPY(To,From) filename_cpy_16bit((To),(From)) # define FILENAME_CHARSIZE 2 @@ -289,6 +326,10 @@ typedef struct { ErlDrvPDL q_mtx; /* Mutex for the driver queue, known by the emulator. Also used for mutual exclusion when accessing field(s) below. */ size_t write_buffered; +#ifdef USE_VM_PROBES + int idnum; /* Unique ID # for this driver thread/desc */ + char port_str[DTRACE_TERM_BUF_SIZE]; +#endif } file_descriptor; @@ -386,6 +427,11 @@ struct t_data void (*free)(void *); int again; int reply; +#ifdef USE_VM_PROBES + int sched_i1; + Uint64 sched_i2; + char sched_utag[DTRACE_EFILE_BUFSIZ+1]; +#endif int result_ok; Efile_error errInfo; int flags; @@ -458,8 +504,6 @@ struct t_data char b[1]; }; - - #define EF_ALLOC(S) driver_alloc((S)) #define EF_REALLOC(P, S) driver_realloc((P), (S)) #define EF_SAFE_ALLOC(S) ef_safe_alloc((S)) @@ -488,7 +532,7 @@ static void *ef_safe_realloc(void *op, Uint s) * ErlIOVec manipulation functions. */ -/* char EV_CHAR(ErlIOVec *ev, int p, int q) */ +/* char EV_CHAR_P(ErlIOVec *ev, int p, int q) */ #define EV_CHAR_P(ev, p, q) \ (((char *)(ev)->iov[(q)].iov_base) + (p)) @@ -683,6 +727,11 @@ file_init(void) : 0); driver_system_info(&sys_info, sizeof(ErlDrvSysInfo)); +#ifdef USE_VM_PROBES + erts_mtx_init(&dt_driver_mutex, "efile_drv dtrace mutex"); + pthread_key_create(&dt_driver_key, NULL); +#endif /* USE_VM_PROBES */ + return 0; } @@ -722,6 +771,10 @@ file_start(ErlDrvPort port, char* command) desc->write_error = 0; MUTEX_INIT(desc->q_mtx, port); /* Refc is one, referenced by emulator now */ desc->write_buffered = 0; +#ifdef USE_VM_PROBES + dtrace_drvport_str(port, desc->port_str); + get_dt_private(0); /* throw away return value */ +#endif /* USE_VM_PROBES */ return (ErlDrvData) desc; } @@ -741,8 +794,10 @@ static void do_close(int flags, SWord fd) { static void invoke_close(void *data) { struct t_data *d = (struct t_data *) data; + DTRACE_INVOKE_SETUP(FILE_CLOSE); d->again = 0; do_close(d->flags, d->fd); + DTRACE_INVOKE_RETURN(FILE_CLOSE); } /********************************************************************* @@ -972,49 +1027,63 @@ static void invoke_name(void *data, int (*f)(Efile_error *, char *)) static void invoke_mkdir(void *data) { + DTRACE_INVOKE_SETUP_BY_NAME(FILE_MKDIR); invoke_name(data, efile_mkdir); + DTRACE_INVOKE_RETURN(FILE_MKDIR); } static void invoke_rmdir(void *data) { + DTRACE_INVOKE_SETUP_BY_NAME(FILE_RMDIR); invoke_name(data, efile_rmdir); + DTRACE_INVOKE_RETURN(FILE_RMDIR); } static void invoke_delete_file(void *data) { + DTRACE_INVOKE_SETUP_BY_NAME(FILE_DELETE); invoke_name(data, efile_delete_file); + DTRACE_INVOKE_RETURN(FILE_DELETE); } static void invoke_chdir(void *data) { + DTRACE_INVOKE_SETUP_BY_NAME(FILE_CHDIR); invoke_name(data, efile_chdir); + DTRACE_INVOKE_RETURN(FILE_CHDIR); } static void invoke_fdatasync(void *data) { struct t_data *d = (struct t_data *) data; int fd = (int) d->fd; + DTRACE_INVOKE_SETUP(FILE_FDATASYNC); d->again = 0; d->result_ok = efile_fdatasync(&d->errInfo, fd); + DTRACE_INVOKE_RETURN(FILE_FDATASYNC); } static void invoke_fsync(void *data) { struct t_data *d = (struct t_data *) data; int fd = (int) d->fd; + DTRACE_INVOKE_SETUP(FILE_FSYNC); d->again = 0; d->result_ok = efile_fsync(&d->errInfo, fd); + DTRACE_INVOKE_RETURN(FILE_FSYNC); } static void invoke_truncate(void *data) { struct t_data *d = (struct t_data *) data; int fd = (int) d->fd; + DTRACE_INVOKE_SETUP(FILE_TRUNCATE); d->again = 0; d->result_ok = efile_truncate_file(&d->errInfo, &fd, d->flags); + DTRACE_INVOKE_RETURN(FILE_TRUNCATE); } static void invoke_read(void *data) @@ -1022,6 +1091,7 @@ static void invoke_read(void *data) struct t_data *d = (struct t_data *) data; int status, segment; size_t size, read_size; + DTRACE_INVOKE_SETUP(FILE_READ); segment = d->again && d->c.read.bin_size >= 2*FILE_SEGMENT_READ; if (segment) { @@ -1056,6 +1126,7 @@ static void invoke_read(void *data) } else { d->again = 0; } + DTRACE_INVOKE_RETURN(FILE_READ); } static void free_read(void *data) @@ -1072,6 +1143,7 @@ static void invoke_read_line(void *data) int status; size_t read_size; int local_loop = (d->again == 0); + DTRACE_INVOKE_SETUP(FILE_READ_LINE); do { size_t size = (d->c.read_line.binp)->orig_size - @@ -1163,6 +1235,7 @@ static void invoke_read_line(void *data) break; } } while (local_loop); + DTRACE_INVOKE_RETURN(FILE_READ_LINE); } static void free_read_line(void *data) @@ -1178,6 +1251,7 @@ static void invoke_read_file(void *data) struct t_data *d = (struct t_data *) data; size_t read_size; int chop; + DTRACE_INVOKE_SETUP(FILE_READ_FILE); if (! d->c.read_file.binp) { /* First invocation only */ int fd; @@ -1214,12 +1288,14 @@ static void invoke_read_file(void *data) &read_size); if (d->result_ok) { d->c.read_file.offset += read_size; - if (chop) return; /* again */ + if (chop) goto chop_done; /* again */ } close: efile_closefile((int) d->fd); done: d->again = 0; + chop_done: + DTRACE_INVOKE_RETURN(FILE_READ_FILE); } static void free_read_file(void *data) @@ -1239,6 +1315,7 @@ static void invoke_preadv(void *data) ErlIOVec *ev = &c->eiov; size_t bytes_read_so_far = 0; unsigned char *p = (unsigned char *)ev->iov[0].iov_base + 4+4+8*c->cnt; + DTRACE_INVOKE_SETUP(FILE_PREADV); while (c->cnt < c->n) { size_t read_size = ev->iov[1 + c->cnt].iov_len - c->size; @@ -1260,7 +1337,7 @@ static void invoke_preadv(void *data) bytes_read_so_far += bytes_read; if (chop && bytes_read == read_size) { c->size += bytes_read; - return; + goto done; } ASSERT(bytes_read <= read_size); ev->iov[1 + c->cnt].iov_len = bytes_read + c->size; @@ -1271,7 +1348,7 @@ static void invoke_preadv(void *data) if (d->again && bytes_read_so_far >= FILE_SEGMENT_READ && c->cnt < c->n) { - return; + goto done; } } else { /* In case of a read error, ev->size will not be correct, @@ -1282,6 +1359,8 @@ static void invoke_preadv(void *data) } } d->again = 0; + done: + DTRACE_INVOKE_RETURN(FILE_PREADV); } static void free_preadv(void *data) { @@ -1303,6 +1382,7 @@ static void invoke_ipread(void *data) size_t bytes_read = 0; char buf[2*sizeof(Uint32)]; Uint32 offset, size; + DTRACE_INVOKE_SETUP(FILE_IPREAD); /* Read indirection header */ if (! efile_pread(&d->errInfo, (int) d->fd, c->offsets[0], @@ -1341,14 +1421,17 @@ static void invoke_ipread(void *data) /* Read data block */ d->invoke = invoke_preadv; invoke_preadv(data); + DTRACE_INVOKE_RETURN(FILE_IPREAD); return; error: d->result_ok = 0; d->again = 0; + DTRACE_INVOKE_RETURN(FILE_IPREAD); return; done: d->result_ok = !0; d->again = 0; + DTRACE_INVOKE_RETURN(FILE_IPREAD); } /* invoke_writev and invoke_pwritev are the only thread functions that @@ -1371,6 +1454,7 @@ static void invoke_writev(void *data) { size_t size; size_t p; int segment; + DTRACE_INVOKE_SETUP(FILE_WRITE); segment = d->again && d->c.writev.size >= 2*FILE_SEGMENT_WRITE; if (segment) { @@ -1444,6 +1528,7 @@ static void invoke_writev(void *data) { TRACE_F(("w%lu", (unsigned long)size)); } + DTRACE_INVOKE_RETURN(FILE_WRITE); } static void free_writev(void *data) { @@ -1457,34 +1542,40 @@ static void free_writev(void *data) { static void invoke_pwd(void *data) { struct t_data *d = (struct t_data *) data; + DTRACE_INVOKE_SETUP(FILE_PWD); d->again = 0; d->result_ok = efile_getdcwd(&d->errInfo,d->drive, d->b+1, RESBUFSIZE-1); + DTRACE_INVOKE_RETURN(FILE_PWD); } static void invoke_readlink(void *data) { struct t_data *d = (struct t_data *) data; char resbuf[RESBUFSIZE]; /* Result buffer. */ + DTRACE_INVOKE_SETUP(FILE_READLINK); d->again = 0; d->result_ok = efile_readlink(&d->errInfo, d->b, resbuf+1, RESBUFSIZE-1); if (d->result_ok != 0) FILENAME_COPY((char *) d->b + 1, resbuf+1); + DTRACE_INVOKE_RETURN(FILE_READLINK); } static void invoke_altname(void *data) { struct t_data *d = (struct t_data *) data; char resbuf[RESBUFSIZE]; /* Result buffer. */ + DTRACE_INVOKE_SETUP(FILE_ALTNAME); d->again = 0; d->result_ok = efile_altname(&d->errInfo, d->b, resbuf+1, RESBUFSIZE-1); if (d->result_ok != 0) FILENAME_COPY((char *) d->b + 1, resbuf+1); + DTRACE_INVOKE_RETURN(FILE_ALTNAME); } static void invoke_pwritev(void *data) { @@ -1497,6 +1588,7 @@ static void invoke_pwritev(void *data) { size_t p; int segment; size_t size, write_size; + DTRACE_INVOKE_SETUP(FILE_PWRITEV); segment = d->again && c->size >= 2*FILE_SEGMENT_WRITE; if (segment) { @@ -1576,6 +1668,7 @@ static void invoke_pwritev(void *data) { } done: EF_FREE(iov); /* Free our copy of the vector, nothing to restore */ + DTRACE_INVOKE_RETURN(FILE_PWRITEV); } static void free_pwritev(void *data) { @@ -1591,9 +1684,14 @@ static void invoke_flstat(void *data) { struct t_data *d = (struct t_data *) data; + DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2, + d->command == FILE_LSTAT ? FILE_LSTAT : FILE_FSTAT); d->again = 0; d->result_ok = efile_fileinfo(&d->errInfo, &d->info, d->b, d->command == FILE_LSTAT); + DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2, + d->command == FILE_LSTAT ? FILE_LSTAT : FILE_FSTAT); + gcc_optimizer_hack++; } static void invoke_link(void *data) @@ -1601,10 +1699,12 @@ static void invoke_link(void *data) struct t_data *d = (struct t_data *) data; char *name = d->b; char *new_name; + DTRACE_INVOKE_SETUP(FILE_LINK); d->again = 0; new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE; d->result_ok = efile_link(&d->errInfo, name, new_name); + DTRACE_INVOKE_RETURN(FILE_LINK); } static void invoke_symlink(void *data) @@ -1612,10 +1712,12 @@ static void invoke_symlink(void *data) struct t_data *d = (struct t_data *) data; char *name = d->b; char *new_name; + DTRACE_INVOKE_SETUP(FILE_SYMLINK); d->again = 0; new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE; d->result_ok = efile_symlink(&d->errInfo, name, new_name); + DTRACE_INVOKE_RETURN(FILE_SYMLINK); } static void invoke_rename(void *data) @@ -1623,24 +1725,29 @@ static void invoke_rename(void *data) struct t_data *d = (struct t_data *) data; char *name = d->b; char *new_name; + DTRACE_INVOKE_SETUP(FILE_RENAME); d->again = 0; new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE; d->result_ok = efile_rename(&d->errInfo, name, new_name); + DTRACE_INVOKE_RETURN(FILE_RENAME); } static void invoke_write_info(void *data) { struct t_data *d = (struct t_data *) data; + DTRACE_INVOKE_SETUP(FILE_WRITE_INFO); d->again = 0; d->result_ok = efile_write_info(&d->errInfo, &d->info, d->b); + DTRACE_INVOKE_RETURN(FILE_WRITE_INFO); } static void invoke_lseek(void *data) { struct t_data *d = (struct t_data *) data; int status; + DTRACE_INVOKE_SETUP(FILE_LSEEK); d->again = 0; if (d->flags & EFILE_COMPRESSED) { @@ -1665,6 +1772,7 @@ static void invoke_lseek(void *data) &d->c.lseek.location); } d->result_ok = status; + DTRACE_INVOKE_RETURN(FILE_LSEEK); } static void invoke_readdir(void *data) @@ -1675,6 +1783,7 @@ static void invoke_readdir(void *data) size_t n = 0, total = 0; struct t_readdir_buf *b = NULL; int res = 0; + DTRACE_INVOKE_SETUP(FILE_READDIR); d->again = 0; d->errInfo.posix_errno = 0; @@ -1710,13 +1819,14 @@ static void invoke_readdir(void *data) } while(res); d->result_ok = (d->errInfo.posix_errno == 0); + DTRACE_INVOKE_RETURN(FILE_READDIR); } static void invoke_open(void *data) { struct t_data *d = (struct t_data *) data; - int status = 1; /* Status of open call. */ + DTRACE_INVOKE_SETUP(FILE_OPEN); d->again = 0; if ((d->flags & EFILE_COMPRESSED) == 0) { @@ -1749,6 +1859,7 @@ static void invoke_open(void *data) } d->result_ok = status; + DTRACE_INVOKE_RETURN(FILE_OPEN); } static void invoke_fadvise(void *data) @@ -1758,9 +1869,11 @@ static void invoke_fadvise(void *data) off_t offset = (off_t) d->c.fadvise.offset; off_t length = (off_t) d->c.fadvise.length; int advise = (int) d->c.fadvise.advise; + DTRACE_INVOKE_SETUP(FILE_FADVISE); d->again = 0; d->result_ok = efile_fadvise(&d->errInfo, fd, offset, length, advise); + DTRACE_INVOKE_RETURN(FILE_FADVISE); } #ifdef HAVE_SENDFILE @@ -1841,6 +1954,7 @@ static void free_readdir(void *data) { struct t_data *d = (struct t_data *) data; struct t_readdir_buf *b1 = d->c.read_dir.first_buf; + while (b1) { struct t_readdir_buf *b2 = b1; b1 = b1->next; @@ -1909,12 +2023,16 @@ static void cq_execute(file_descriptor *desc) { DRIVER_ASYNC(d->level, desc, d->invoke, void_ptr=d, d->free); } -static int async_write(file_descriptor *desc, int *errp, - int reply, Uint32 reply_size) { +static struct t_data *async_write(file_descriptor *desc, int *errp, + int reply, Uint32 reply_size +#ifdef USE_VM_PROBES + ,Sint64 *dt_i1, Sint64 *dt_i2, Sint64 *dt_i3 +#endif +) { struct t_data *d; if (! (d = EF_ALLOC(sizeof(struct t_data) - 1))) { if (errp) *errp = ENOMEM; - return -1; + return NULL; } TRACE_F(("w%lu", (unsigned long)desc->write_buffered)); d->command = FILE_WRITE; @@ -1923,6 +2041,13 @@ static int async_write(file_descriptor *desc, int *errp, d->c.writev.port = desc->port; d->c.writev.q_mtx = desc->q_mtx; d->c.writev.size = desc->write_buffered; +#ifdef USE_VM_PROBES + if (dt_i1 != NULL) { + *dt_i1 = d->fd; + *dt_i2 = d->flags; + *dt_i3 = d->c.writev.size; + } +#endif d->reply = reply; d->c.writev.free_size = 0; d->c.writev.reply_size = reply_size; @@ -1931,18 +2056,49 @@ static int async_write(file_descriptor *desc, int *errp, d->level = 1; cq_enq(desc, d); desc->write_buffered = 0; - return 0; + return d; } -static int flush_write(file_descriptor *desc, int *errp) { - int result; +static int flush_write(file_descriptor *desc, int *errp +#ifdef USE_VM_PROBES + , dt_private *dt_priv, char *dt_utag +#endif +) { + int result = 0; +#ifdef USE_VM_PROBES + Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0; +#endif + struct t_data *d = NULL; + MUTEX_LOCK(desc->q_mtx); if (desc->write_buffered > 0) { - result = async_write(desc, errp, 0, 0); - } else { - result = 0; + if ((d = async_write(desc, errp, 0, 0 +#ifdef USE_VM_PROBES + ,&dt_i1, &dt_i2, &dt_i3 +#endif + )) == NULL) { + result = -1; + } } MUTEX_UNLOCK(desc->q_mtx); +#ifdef USE_VM_PROBES + if (d != NULL) { + d->sched_i1 = dt_priv->thread_num; + d->sched_i2 = dt_priv->tag; + d->sched_utag[0] = '\0'; + if (dt_utag != NULL) { + if (dt_utag[0] == '\0') { + dt_utag = NULL; + } else { + strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1); + d->sched_utag[sizeof(d->sched_utag) - 1] = '\0'; + } + } + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++, + dt_utag, FILE_WRITE, + NULL, NULL, dt_i1, dt_i2, dt_i3, 0, desc->port_str); + } +#endif /* USE_VM_PROBES */ return result; } @@ -1955,9 +2111,17 @@ static int check_write_error(file_descriptor *desc, int *errp) { return 0; } -static int flush_write_check_error(file_descriptor *desc, int *errp) { +static int flush_write_check_error(file_descriptor *desc, int *errp +#ifdef USE_VM_PROBES + , dt_private *dt_priv, char *dt_utag +#endif + ) { int r; - if ( (r = flush_write(desc, errp)) != 0) { + if ( (r = flush_write(desc, errp +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + )) != 0) { check_write_error(desc, NULL); return r; } else { @@ -1965,12 +2129,16 @@ static int flush_write_check_error(file_descriptor *desc, int *errp) { } } -static int async_lseek(file_descriptor *desc, int *errp, int reply, - Sint64 offset, int origin) { +static struct t_data *async_lseek(file_descriptor *desc, int *errp, int reply, + Sint64 offset, int origin +#ifdef USE_VM_PROBES + , Sint64 *dt_i1, Sint64 *dt_i2, Sint64 *dt_i3 +#endif + ) { struct t_data *d; if (! (d = EF_ALLOC(sizeof(struct t_data)))) { *errp = ENOMEM; - return -1; + return NULL; } d->flags = desc->flags; d->fd = desc->fd; @@ -1978,11 +2146,18 @@ static int async_lseek(file_descriptor *desc, int *errp, int reply, d->reply = reply; d->c.lseek.offset = offset; d->c.lseek.origin = origin; +#ifdef USE_VM_PROBES + if (dt_i1 != NULL) { + *dt_i1 = d->fd; + *dt_i2 = d->c.lseek.offset; + *dt_i3 = d->c.lseek.origin; + } +#endif d->invoke = invoke_lseek; d->free = free_data; d->level = 1; cq_enq(desc, d); - return 0; + return d; } static void flush_read(file_descriptor *desc) { @@ -1994,18 +2169,45 @@ static void flush_read(file_descriptor *desc) { } } -static int lseek_flush_read(file_descriptor *desc, int *errp) { +static int lseek_flush_read(file_descriptor *desc, int *errp +#ifdef USE_VM_PROBES + ,dt_private *dt_priv, char *dt_utag +#endif + ) { int r = 0; size_t read_size = desc->read_size; +#ifdef USE_VM_PROBES + Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0; +#endif + struct t_data *d; + + flush_read(desc); if (read_size != 0) { - flush_read(desc); - if ((r = async_lseek(desc, errp, 0, - -((ssize_t)read_size), EFILE_SEEK_CUR)) - < 0) { - return r; - } - } else { - flush_read(desc); + if ((d = async_lseek(desc, errp, 0, + -((ssize_t)read_size), EFILE_SEEK_CUR +#ifdef USE_VM_PROBES + , &dt_i1, &dt_i2, &dt_i3 +#endif + )) == NULL) { + r = -1; + } else { +#ifdef USE_VM_PROBES + d->sched_i1 = dt_priv->thread_num; + d->sched_i2 = dt_priv->tag; + d->sched_utag[0] = '\0'; + if (dt_utag != NULL) { + if (dt_utag[0] == '\0') { + dt_utag = NULL; + } else { + strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1); + d->sched_utag[sizeof(d->sched_utag) - 1] = '\0'; + } + } + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++, + dt_utag, FILE_LSEEK, + NULL, NULL, dt_i1, dt_i2, dt_i3, 0, desc->port_str); +#endif /* USE_VM_PROBES */ + } } return r; } @@ -2022,11 +2224,23 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) struct t_data *d = (struct t_data *) data; char header[5]; /* result code + count */ char resbuf[RESBUFSIZE]; /* Result buffer. */ - +#ifdef USE_VM_PROBES + int sched_i1 = d->sched_i1, sched_i2 = d->sched_i2, command = d->command, + result_ok = d->result_ok, + posix_errno = d->result_ok ? 0 : d->errInfo.posix_errno; + DTRACE_CHARBUF(sched_utag, DTRACE_EFILE_BUFSIZ+1); + + sched_utag[0] = '\0'; + if (DTRACE_ENABLED(efile_drv_return)) { + strncpy(sched_utag, d->sched_utag, DTRACE_EFILE_BUFSIZ); + sched_utag[DTRACE_EFILE_BUFSIZ] = '\0'; + } +#endif /* USE_VM_PROBES */ TRACE_C('r'); if (try_again(desc, d)) { + /* DTRACE TODO: what kind of probe makes sense here? */ return; } @@ -2224,6 +2438,9 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) if (d->reply) { TRACE_C('K'); reply_ok(desc); +#ifdef USE_VM_PROBES + result_ok = 1; +#endif } free_data(data); break; @@ -2279,6 +2496,8 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) default: abort(); } + DTRACE6(efile_drv_return, sched_i1, sched_i2, sched_utag, + command, result_ok, posix_errno); if (desc->write_buffered != 0 && desc->timer_state == timer_idle) { desc->timer_state = timer_write; driver_set_timer(desc->port, desc->write_delay); @@ -2301,7 +2520,15 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) char* name; /* Points to the filename in buf. */ int command; struct t_data *d = NULL; - +#ifdef USE_VM_PROBES + char *dt_utag = NULL; + char *dt_s1 = NULL, *dt_s2 = NULL; + Sint64 dt_i1 = 0; + Sint64 dt_i2 = 0; + Sint64 dt_i3 = 0; + Sint64 dt_i4 = 0; + dt_private *dt_priv = get_dt_private(0); +#endif /* USE_VM_PROBES */ TRACE_C('o'); @@ -2316,6 +2543,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_mkdir; d->free = free_data; @@ -2327,6 +2558,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_rmdir; d->free = free_data; @@ -2338,6 +2573,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_delete_file; d->free = free_data; @@ -2355,6 +2594,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) FILENAME_COPY(d->b, name); FILENAME_COPY(d->b + namelen, new_name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_s2 = d->b + namelen; + dt_utag = buf + namelen + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->flags = desc->flags; d->fd = fd; d->command = command; @@ -2368,6 +2612,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_chdir; d->free = free_data; @@ -2379,6 +2627,9 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1); d->drive = *(uchar*)buf; +#ifdef USE_VM_PROBES + dt_utag = buf + 1; +#endif d->command = command; d->invoke = invoke_pwd; d->free = free_data; @@ -2394,6 +2645,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->dir_handle = NULL; d->command = command; d->invoke = invoke_readdir; @@ -2418,6 +2673,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) dir_handle = NULL; resbuf[0] = FILE_RESP_LFNAME; +#ifdef USE_VM_PROBES + dt_s1 = name; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif /* Fill the buffer with multiple directory listings before sending it to the * receiving process. READDIR_CHUNKS is minimum number of files sent to the * receiver. @@ -2451,6 +2710,17 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) reply_error(desc, &errInfo); return; } +#ifdef USE_VM_PROBES + if (dt_utag != NULL && dt_utag[0] == '\0') { + dt_utag = NULL; + } + + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag, + dt_utag, command, name, dt_s2, + dt_i1, dt_i2, dt_i3, dt_i4, desc->port_str); + DTRACE6(efile_drv_return, dt_priv->thread_num, dt_priv->tag++, + dt_utag, command, 1, 0); +#endif TRACE_C('R'); driver_output2(desc->port, resbuf, 1, NULL, 0); return; @@ -2463,6 +2733,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d->flags = get_int32((uchar*)buf); name = buf+4; FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_i1 = d->flags; + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_open; d->free = free_data; @@ -2475,6 +2750,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data)); d->fd = fd; +#ifdef USE_VM_PROBES + dt_utag = name; + dt_i1 = fd; +#endif d->command = command; d->invoke = invoke_fdatasync; d->free = free_data; @@ -2487,6 +2766,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data)); d->fd = fd; +#ifdef USE_VM_PROBES + dt_utag = name; + dt_i1 = fd; +#endif d->command = command; d->invoke = invoke_fsync; d->free = free_data; @@ -2503,6 +2786,14 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) FILENAME_COPY(d->b, name); d->fd = fd; +#ifdef USE_VM_PROBES + dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; + if (command == FILE_LSTAT) { + dt_s1 = d->b; + } else { + dt_i1 = fd; + } +#endif d->command = command; d->invoke = invoke_flstat; d->free = free_data; @@ -2516,6 +2807,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d->flags = desc->flags; d->fd = fd; +#ifdef USE_VM_PROBES + dt_utag = name; + dt_i1 = fd; + dt_i2 = d->flags; +#endif d->command = command; d->invoke = invoke_truncate; d->free = free_data; @@ -2536,6 +2832,13 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d->info.cTime = (time_t)((Sint64)get_int64(buf + 7 * 4)); FILENAME_COPY(d->b, buf + 9*4); +#ifdef USE_VM_PROBES + dt_i1 = d->info.mode; + dt_i2 = d->info.uid; + dt_i3 = d->info.gid; + dt_s1 = d->b; + dt_utag = buf + 9 * 4 + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_write_info; d->free = free_data; @@ -2548,6 +2851,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_readlink; d->free = free_data; @@ -2559,6 +2866,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) { d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_altname; d->free = free_data; @@ -2579,6 +2890,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) FILENAME_COPY(d->b, name); FILENAME_COPY(d->b + namelen, new_name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_s2 = d->b + namelen; + dt_utag = buf + namelen + FILENAME_BYTELEN(dt_s2) + FILENAME_CHARSIZE; +#endif d->flags = desc->flags; d->fd = fd; d->command = command; @@ -2600,6 +2916,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) FILENAME_COPY(d->b, name); FILENAME_COPY(d->b + namelen, new_name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_s2 = d->b + namelen; + dt_utag = buf + namelen + FILENAME_BYTELEN(dt_s2) + FILENAME_CHARSIZE; +#endif d->flags = desc->flags; d->fd = fd; d->command = command; @@ -2621,6 +2942,13 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d->c.fadvise.offset = get_int64((uchar*) buf); d->c.fadvise.length = get_int64(((uchar*) buf) + sizeof(Sint64)); d->c.fadvise.advise = get_int32(((uchar*) buf) + 2 * sizeof(Sint64)); +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->c.fadvise.offset; + dt_i3 = d->c.fadvise.length; + dt_i4 = d->c.fadvise.advise; + dt_utag = buf + 3 * sizeof(Sint64); +#endif goto done; } @@ -2634,6 +2962,22 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) done: if (d) { +#ifdef USE_VM_PROBES + d->sched_i1 = dt_priv->thread_num; + d->sched_i2 = dt_priv->tag; + d->sched_utag[0] = '\0'; + if (dt_utag != NULL) { + if (dt_utag[0] == '\0') { + dt_utag = NULL; + } else { + strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1); + d->sched_utag[sizeof(d->sched_utag) - 1] = '\0'; + } + } + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++, + dt_utag, command, dt_s1, dt_s2, + dt_i1, dt_i2, dt_i3, dt_i4, desc->port_str); +#endif cq_enq(desc, d); } } @@ -2647,6 +2991,9 @@ file_flush(ErlDrvData e) { #ifdef DEBUG int r; #endif +#ifdef USE_VM_PROBES + dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base); +#endif TRACE_C('f'); @@ -2657,7 +3004,11 @@ file_flush(ErlDrvData e) { #ifdef DEBUG r = #endif - flush_write(desc, NULL); + flush_write(desc, NULL +#ifdef USE_VM_PROBES + , dt_priv, (desc->d == NULL) ? NULL : desc->d->sched_utag +#endif + ); /* Only possible reason for bad return value is ENOMEM, and * there is nobody to tell... */ @@ -2699,6 +3050,9 @@ static void file_timeout(ErlDrvData e) { file_descriptor *desc = (file_descriptor *)e; enum e_timer timer_state = desc->timer_state; +#ifdef USE_VM_PROBES + dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base); +#endif TRACE_C('t'); @@ -2716,7 +3070,11 @@ file_timeout(ErlDrvData e) { #ifdef DEBUG int r = #endif - flush_write(desc, NULL); + flush_write(desc, NULL +#ifdef USE_VM_PROBES + , dt_priv, (desc->d == NULL) ? NULL : desc->d->sched_utag +#endif + ); /* Only possible reason for bad return value is ENOMEM, and * there is nobody to tell... */ @@ -2737,6 +3095,14 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { char command; int p, q; int err; + struct t_data *d = NULL; +#ifdef USE_VM_PROBES + Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0; + Sint64 dt_i4 = 0; + char *dt_utag = NULL; + char *dt_s1 = NULL; + dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base); +#endif TRACE_C('v'); @@ -2756,18 +3122,19 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { switch (command) { case FILE_CLOSE: { +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif flush_read(desc); - if (flush_write_check_error(desc, &err) < 0) { + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (ev->size != 1) { - /* Wrong command length */ - reply_posix_error(desc, EINVAL); - goto done; - } if (desc->fd != FILE_FD_INVALID) { - struct t_data *d; if (! (d = EF_ALLOC(sizeof(struct t_data)))) { reply_posix_error(desc, ENOMEM); } else { @@ -2775,6 +3142,10 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->reply = !0; d->fd = desc->fd; d->flags = desc->flags; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; +#endif d->invoke = invoke_close; d->free = free_data; d->level = 2; @@ -2790,8 +3161,21 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { case FILE_READ: { Uint32 sizeH, sizeL; size_t size, alloc_size; - struct t_data *d; - if (flush_write_check_error(desc, &err) < 0) { + + if (!EV_GET_UINT32(ev, &sizeH, &p, &q) + || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { + /* Wrong buffer length to contain the read count */ + reply_posix_error(desc, EINVAL); + goto done; + } +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } @@ -2799,19 +3183,16 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { if (desc->read_bufsize == 0 && desc->read_binp != NULL && desc->read_size > 0) { /* We have allocated a buffer for line mode but should not really have a read-ahead buffer... */ - if (lseek_flush_read(desc, &err) < 0) { + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } } #endif - if (ev->size != 1+8 - || !EV_GET_UINT32(ev, &sizeH, &p, &q) - || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { - /* Wrong buffer length to contain the read count */ - reply_posix_error(desc, EINVAL); - goto done; - } #if SIZEOF_SIZE_T == 4 if (sizeH != 0) { reply_posix_error(desc, EINVAL); @@ -2887,6 +3268,11 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->c.read.bin_offset = desc->read_offset + desc->read_size; d->c.read.bin_size = desc->read_binp->orig_size - d->c.read.bin_offset; d->c.read.size = size; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; + dt_i3 = d->c.read.size; +#endif driver_binary_inc_refc(d->c.read.binp); d->invoke = invoke_read; d->free = free_read; @@ -2904,12 +3290,22 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { * allocated binary + dealing with offsets and lengts are done in file_async ready * for this OP. */ - struct t_data *d; - if (flush_write_check_error(desc, &err) < 0) { +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (ev->size != 1) { + if (ev->size != 1 +#ifdef USE_VM_PROBES + + FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE +#endif + ) { /* Wrong command length */ reply_posix_error(desc, EINVAL); goto done; @@ -2965,8 +3361,16 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->c.read_line.binp = desc->read_binp; d->c.read_line.read_offset = desc->read_offset; d->c.read_line.read_size = desc->read_size; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; + dt_i3 = d->c.read_line.read_offset; +#endif #if !ALWAYS_READ_LINE_AHEAD d->c.read_line.read_ahead = (desc->read_bufsize > 0); +#ifdef USE_VM_PROBES + dt_i4 = d->c.read_line.read_ahead; +#endif #endif driver_binary_inc_refc(d->c.read.binp); d->invoke = invoke_read_line; @@ -2974,10 +3378,22 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->level = 1; cq_enq(desc, d); } goto done; - case FILE_WRITE: { + case FILE_WRITE: { /* Dtrace: The dtrace user tag is not last in message, + but follows the message tag directly. + This is handled specially in prim_file.erl */ ErlDrvSizeT skip = 1; ErlDrvSizeT size = ev->size - skip; - if (lseek_flush_read(desc, &err) < 0) { + +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); + skip += FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE; + size = ev->size - skip; +#endif + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } @@ -3004,7 +3420,11 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { driver_set_timer(desc->port, desc->write_delay); } } else { - if (async_write(desc, &err, !0, size) != 0) { + if ((d = async_write(desc, &err, !0, size +#ifdef USE_VM_PROBES + , &dt_i1, &dt_i2, &dt_i3 +#endif + )) == NULL) { MUTEX_UNLOCK(desc->q_mtx); reply_posix_error(desc, err); goto done; @@ -3014,24 +3434,49 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } } goto done; /* case FILE_WRITE */ - case FILE_PWRITEV: { + case FILE_PWRITEV: { /* Dtrace: The dtrace user tag is not last in message, + but follows the message tag directly. + This is handled specially in prim_file.erl */ Uint32 i, j, n; size_t total; - struct t_data *d; - if (lseek_flush_read(desc, &err) < 0) { - reply_Uint_posix_error(desc, 0, err); - goto done; - } - if (flush_write_check_error(desc, &err) < 0) { - reply_Uint_posix_error(desc, 0, err); - goto done; +#ifdef USE_VM_PROBES + char dt_tmp; + int dt_utag_bytes = 1; + + dt_utag = EV_CHAR_P(ev, p, q); + /* This will work for UTF-8, but not for UTF-16 - extra reminder here */ +#ifdef FILENAMES_16BIT +#error 16bit characters in filenames and dtrace in combination is not supported. +#endif + while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') { + dt_utag_bytes++; } +#endif if (ev->size < 1+4 +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif || !EV_GET_UINT32(ev, &n, &p, &q)) { /* Buffer too short to contain even the number of pos/size specs */ reply_Uint_posix_error(desc, 0, EINVAL); goto done; } + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_Uint_posix_error(desc, 0, err); + goto done; + } + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_Uint_posix_error(desc, 0, err); + goto done; + } if (n == 0) { /* Trivial case - nothing to write */ if (ev->size != 1+4) { @@ -3041,7 +3486,11 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } goto done; } - if (ev->size < 1+4+8*(2*n)) { + if (ev->size < 1+4+8*(2*n) +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif + ) { /* Buffer too short to contain even the pos/size specs */ reply_Uint_posix_error(desc, 0, EINVAL); goto done; @@ -3056,6 +3505,10 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->reply = !0; d->fd = desc->fd; d->flags = desc->flags; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; +#endif d->c.pwritev.port = desc->port; d->c.pwritev.q_mtx = desc->q_mtx; d->c.pwritev.n = n; @@ -3093,13 +3546,20 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } } d->c.pwritev.size = total; +#ifdef USE_VM_PROBES + dt_i3 = d->c.pwritev.size; +#endif d->c.pwritev.free_size = 0; if (j == 0) { /* Trivial case - nothing to write */ EF_FREE(d); reply_Uint(desc, 0); } else { - ErlDrvSizeT skip = 1 + 4 + 8*(2*n); + ErlDrvSizeT skip = 1 + 4 + 8 * (2*n) +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif + ; if (skip + total != ev->size) { /* Actual amount of data does not match * total of all pos/size specs @@ -3120,27 +3580,55 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } } goto done; /* case FILE_PWRITEV: */ - case FILE_PREADV: { + case FILE_PREADV: { /* Dtrace: The dtrace user tag is not last in message, + but follows the message tag directly. + This is handled specially in prim_file.erl */ register void * void_ptr; Uint32 i, n; - struct t_data *d; ErlIOVec *res_ev; - if (lseek_flush_read(desc, &err) < 0) { +#ifdef USE_VM_PROBES + char dt_tmp; + int dt_utag_bytes = 1; + /* This will work for UTF-8, but not for UTF-16 - extra reminder here */ +#ifdef FILENAMES_16BIT +#error 16bit characters in filenames and dtrace in combination is not supported. +#endif + dt_utag = EV_CHAR_P(ev, p, q); + while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') { + dt_utag_bytes++; + } +#endif + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (flush_write_check_error(desc, &err) < 0) { + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } if (ev->size < 1+8 +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif || !EV_GET_UINT32(ev, &n, &p, &q) || !EV_GET_UINT32(ev, &n, &p, &q)) { /* Buffer too short to contain even the number of pos/size specs */ reply_posix_error(desc, EINVAL); goto done; } - if (ev->size != 1+8+8*(2*n)) { + if (ev->size < 1+8+8*(2*n) +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif + ) { /* Buffer wrong length to contain the pos/size specs */ reply_posix_error(desc, EINVAL); goto done; @@ -3160,6 +3648,10 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->reply = !0; d->fd = desc->fd; d->flags = desc->flags; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; +#endif d->c.preadv.n = n; d->c.preadv.cnt = 0; d->c.preadv.size = 0; @@ -3187,6 +3679,9 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { #else size = ((size_t)sizeH<<32) | sizeL; #endif +#ifdef USE_VM_PROBES + dt_i3 += size; +#endif if (! (res_ev->binv[i] = driver_alloc_binary(size))) { reply_posix_error(desc, ENOMEM); break; @@ -3233,42 +3728,68 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } goto done; /* case FILE_PREADV: */ case FILE_LSEEK: { - Sint64 offset; /* Offset for seek */ + Sint64 offset; /* Offset for seek */ Uint32 origin; /* Origin of seek. */ - if (lseek_flush_read(desc, &err) < 0) { - reply_posix_error(desc, err); + + if (ev->size < 1+8+4 + || !EV_GET_UINT64(ev, &offset, &p, &q) + || !EV_GET_UINT32(ev, &origin, &p, &q)) { + /* Wrong length of buffer to contain offset and origin */ + reply_posix_error(desc, EINVAL); goto done; } - if (flush_write_check_error(desc, &err) < 0) { +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (ev->size != 1+8+4 - || !EV_GET_UINT64(ev, &offset, &p, &q) - || !EV_GET_UINT32(ev, &origin, &p, &q)) { - /* Wrong length of buffer to contain offset and origin */ - reply_posix_error(desc, EINVAL); + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_posix_error(desc, err); goto done; } - if (async_lseek(desc, &err, !0, offset, origin) < 0) { + if ((d = async_lseek(desc, &err, !0, offset, origin +#ifdef USE_VM_PROBES + , &dt_i1, &dt_i2, &dt_i3 +#endif + )) == NULL) { reply_posix_error(desc, err); goto done; } } goto done; case FILE_READ_FILE: { - struct t_data *d; char *filename; if (ev->size < 1+1) { /* Buffer contains empty name */ reply_posix_error(desc, ENOENT); goto done; } +#ifndef USE_VM_PROBES + /* In the dtrace case, the iov has an extra element, the dtrace utag - we will need + another test to see that + the filename is in a single buffer: */ if (ev->size-1 != ev->iov[q].iov_len-p) { /* Name not in one single buffer */ reply_posix_error(desc, EINVAL); goto done; } +#else + if (((byte *)ev->iov[q].iov_base)[ev->iov[q].iov_len-1] != '\0') { + /* Name not in one single buffer */ + reply_posix_error(desc, EINVAL); + goto done; + } +#endif filename = EV_CHAR_P(ev, p, q); d = EF_ALLOC(sizeof(struct t_data) -1 + FILENAME_BYTELEN(filename) + FILENAME_CHARSIZE); if (! d) { @@ -3279,6 +3800,20 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->reply = !0; /* Copy name */ FILENAME_COPY(d->b, filename); +#ifdef USE_VM_PROBES + { + char dt_tmp; + + /* This will work for UTF-8, but not for UTF-16 - extra reminder here */ +#ifdef FILENAMES_16BIT +#error 16bit characters in filenames and dtrace in combination is not supported. +#endif + while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') + ; + dt_s1 = d->b; + dt_utag = EV_CHAR_P(ev, p, q); + } +#endif d->c.read_file.binp = NULL; d->invoke = invoke_read_file; d->free = free_read_file; @@ -3298,7 +3833,6 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { char mode; Sint64 hdr_offset; Uint32 max_size; - struct t_data *d; ErlIOVec *res_ev; int vsize; if (! EV_GET_CHAR(ev, &mode, &p, &q)) { @@ -3310,14 +3844,6 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { reply_posix_error(desc, EINVAL); goto done; } - if (lseek_flush_read(desc, &err) < 0) { - reply_posix_error(desc, err); - goto done; - } - if (flush_write_check_error(desc, &err) < 0) { - reply_posix_error(desc, err); - goto done; - } if (ev->size < 1+1+8+4 || !EV_GET_UINT64(ev, &hdr_offset, &p, &q) || !EV_GET_UINT32(ev, &max_size, &p, &q)) { @@ -3326,6 +3852,25 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { reply_posix_error(desc, EINVAL); goto done; } +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_posix_error(desc, err); + goto done; + } + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_posix_error(desc, err); + goto done; + } /* Create the thread data structure with the contained ErlIOVec * and corresponding binaries for the response */ @@ -3342,6 +3887,12 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->flags = desc->flags; d->c.preadv.offsets[0] = hdr_offset; d->c.preadv.size = max_size; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; + dt_i3 = d->c.preadv.offsets[0]; + dt_i4 = d->c.preadv.size; +#endif res_ev = &d->c.preadv.eiov; /* XXX possible alignment problems here for weird machines */ res_ev->iov = void_ptr = d + 1; @@ -3356,16 +3907,24 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { case FILE_SETOPT: { char opt; + if (ev->size < 1+1 || !EV_GET_CHAR(ev, &opt, &p, &q)) { /* Buffer too short to contain even the option type */ reply_posix_error(desc, EINVAL); goto done; } +#ifdef USE_VM_PROBES + dt_i1 = opt; + dt_utag = EV_CHAR_P(ev, p, q); +#endif switch (opt) { case FILE_OPT_DELAYED_WRITE: { Uint32 sizeH, sizeL, delayH, delayL; if (ev->size != 1+1+4*sizeof(Uint32) +#ifdef USE_VM_PROBES + + FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE +#endif || !EV_GET_UINT32(ev, &sizeH, &p, &q) || !EV_GET_UINT32(ev, &sizeL, &p, &q) || !EV_GET_UINT32(ev, &delayH, &p, &q) @@ -3392,12 +3951,18 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { #else desc->write_delay = ((unsigned long)delayH << 32) | delayL; #endif +#ifdef USE_VM_PROBES + dt_i2 = desc->write_delay; +#endif TRACE_C('K'); reply_ok(desc); } goto done; case FILE_OPT_READ_AHEAD: { Uint32 sizeH, sizeL; if (ev->size != 1+1+2*sizeof(Uint32) +#ifdef USE_VM_PROBES + + FILENAME_BYTELEN(dt_utag)+FILENAME_CHARSIZE +#endif || !EV_GET_UINT32(ev, &sizeH, &p, &q) || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { /* Buffer has wrong length to contain the option values */ @@ -3413,6 +3978,9 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { #else desc->read_bufsize = ((size_t)sizeH << 32) | sizeL; #endif +#ifdef USE_VM_PROBES + dt_i2 = desc->read_bufsize; +#endif TRACE_C('K'); reply_ok(desc); } goto done; @@ -3499,11 +4067,19 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } /* switch(command) */ - if (lseek_flush_read(desc, &err) < 0) { + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (flush_write_check_error(desc, &err) < 0) { + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } else { @@ -3521,5 +4097,50 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } done: + if (d != NULL) { +#ifdef USE_VM_PROBES + /* + * If d == NULL, then either: + * 1). There was an error of some sort, or + * 2). The command given to us is actually implemented + * by file_output() instead. + * + * Case #1 is probably a TODO item, perhaps? + * Case #2 we definitely don't want to activate a probe. + */ + d->sched_i1 = dt_priv->thread_num; + d->sched_i2 = dt_priv->tag; + d->sched_utag[0] = '\0'; + if (dt_utag != NULL) { + if (dt_utag[0] == '\0') { + dt_utag = NULL; + } else { + strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1); + d->sched_utag[sizeof(d->sched_utag) - 1] = '\0'; + } + } + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++, + dt_utag, command, dt_s1, NULL, dt_i1, dt_i2, dt_i3, dt_i4, + desc->port_str); +#endif + } cq_execute(desc); } + +#ifdef USE_VM_PROBES +dt_private * +get_dt_private(int base) +{ + dt_private *dt_priv = (dt_private *) pthread_getspecific(dt_driver_key); + + if (dt_priv == NULL) { + dt_priv = EF_SAFE_ALLOC(sizeof(dt_private)); + erts_mtx_lock(&dt_driver_mutex); + dt_priv->thread_num = (base + dt_driver_idnum++); + erts_mtx_unlock(&dt_driver_mutex); + dt_priv->tag = 0; + pthread_setspecific(dt_driver_key, dt_priv); + } + return dt_priv; +} +#endif /* USE_VM_PROBES */ diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 23a4bf1b04..1d173a758a 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -36,6 +36,7 @@ #include "global.h" #include "erl_check_io.h" #include "erl_thr_progress.h" +#include "dtrace-wrapper.h" #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS # define ERTS_DRV_EV_STATE_EXTRA_SIZE 128 @@ -314,6 +315,7 @@ forget_removed(struct pollset_info* psi) erts_smp_mtx_unlock(mtx); if (drv_ptr) { int was_unmasked = erts_block_fpe(); + DTRACE1(driver_stop_select, drv_ptr->name); (*drv_ptr->stop_select) ((ErlDrvEvent) fd, NULL); erts_unblock_fpe(was_unmasked); if (drv_ptr->handle) { @@ -496,6 +498,9 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, ErtsDrvEventState *state; int wake_poller; int ret; +#ifdef USE_VM_PROBES + DTRACE_CHARBUF(name, 64); +#endif ERTS_SMP_LC_ASSERT(erts_drvport2port(ix) && erts_lc_is_port_locked(erts_drvport2port(ix))); @@ -525,6 +530,10 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, if (IS_FD_UNKNOWN(state)) { /* fast track to stop_select callback */ stop_select_fn = erts_drvport2port(ix)->drv_ptr->stop_select; +#ifdef USE_VM_PROBES + strncpy(name, erts_drvport2port(ix)->drv_ptr->name, sizeof(name)-1); + name[sizeof(name)-1] = '\0'; +#endif ret = 0; goto done_unknown; } @@ -661,6 +670,10 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, /* Safe to close fd now as it is not in pollset or there was no need to eject fd (kernel poll) */ stop_select_fn = drv_ptr->stop_select; +#ifdef USE_VM_PROBES + strncpy(name, erts_drvport2port(ix)->drv_ptr->name, sizeof(name)-1); + name[sizeof(name)-1] = '\0'; +#endif } else { /* Not safe to close fd, postpone stop_select callback. */ @@ -686,6 +699,7 @@ done_unknown: erts_smp_mtx_unlock(fd_mtx(fd)); if (stop_select_fn) { int was_unmasked = erts_block_fpe(); + DTRACE1(driver_stop_select, name); (*stop_select_fn)(e, NULL); erts_unblock_fpe(was_unmasked); } diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 58c36c3bdc..ea57000c82 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -28,6 +28,7 @@ my $verbose = 0; my $hot = 1; my $num_file_opcodes = 0; my $wordsize = 32; +my %defs; # Defines (from command line). # This is shift counts and mask for the packer. my $WHOLE_WORD = ''; @@ -96,6 +97,12 @@ my %unnumbered; my %is_transformed; # +# Pre-processor. +# +my @if_val; +my @if_line; + +# # Code transformations. # my $te_max_vars = 0; # Max number of variables ever needed. @@ -223,6 +230,7 @@ while (@ARGV && $ARGV[0] =~ /^-(.*)/) { ($outdir = shift), next if /^outdir/; ($wordsize = shift), next if /^wordsize/; ($verbose = 1), next if /^v/; + ($defs{$1} = $2), next if /^D(\w+)=(\w+)/; die "$0: Bad option: -$_\n"; } @@ -239,7 +247,43 @@ while (<>) { } next if /^\s*$/; next if /^\#/; - + + # + # Handle %if. + # + if (/^\%if (\w+)/) { + my $name = $1; + my $val = $defs{$name}; + defined $val or error("'$name' is undefined"); + push @if_val, $val; + push @if_line, $.; + next; + } elsif (/^\%unless (\w+)/) { + my $name = $1; + my $val = $defs{$name}; + defined $val or error("'$name' is undefined"); + push @if_val, !$val; + push @if_line, $.; + next; + } elsif (/^\%else$/) { + unless (@if_line) { + error("%else without a preceding %if/%unless"); + } + $if_line[$#if_line] = $.; + $if_val[$#if_val] = !$if_val[$#if_val]; + next; + } elsif (/^\%endif$/) { + unless (@if_line) { + error("%endif without a preceding %if/%unless/%else"); + } + pop @if_val; + pop @if_line; + next; + } + if (@if_val and not $if_val[$#if_val]) { + next; + } + # # Handle assignments. # @@ -349,7 +393,13 @@ while (<>) { $unnumbered{$name,$arity} = 1; } } continue { - close(ARGV) if eof(ARGV); + if (eof(ARGV)) { + close(ARGV); + if (@if_line) { + error("Unterminated %if/%unless/%else at " . + "line $if_line[$#if_line]\n"); + } + } } $num_file_opcodes = @gen_opname; |