From b937b42d2f98c4e0024418dbbd55ff545bcf65d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Sat, 10 Apr 2010 17:27:24 +0200 Subject: Call time breakpoint tracing framework Initial commit with a new breakpoint instruction and PSD areas for temporary time storage during tracing. --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/beam_bp.c | 94 +++++++++++++++++++++++++++++++++++++- erts/emulator/beam/beam_bp.h | 41 +++++++++++++++++ erts/emulator/beam/beam_emu.c | 16 +++++++ erts/emulator/beam/erl_bif_trace.c | 70 +++++++++++++++++++++++----- erts/emulator/beam/erl_process.h | 9 +++- erts/emulator/beam/global.h | 1 + erts/emulator/beam/ops.tab | 1 + 8 files changed, 220 insertions(+), 13 deletions(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 1138c0c871..28f69b9460 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -123,6 +123,7 @@ atom busy_dist_port atom busy_port atom call atom call_count +atom call_time atom caller atom capture atom case_clause diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 6278ff6bad..a78645a9c7 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -191,7 +191,12 @@ erts_set_count_break(Eterm mfa[3], int specified, enum erts_break_op count_op) { (BeamInstr) BeamOp(op_i_count_breakpoint), count_op, NIL); } - +int +erts_set_time_break(Eterm mfa[3], int specified, enum erts_break_op count_op) { + ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0)); + return set_break(mfa, specified, NULL, + (BeamInstr) BeamOp(op_i_time_breakpoint), count_op, NIL); +} int erts_clear_trace_break(Eterm mfa[3], int specified) { @@ -236,6 +241,13 @@ erts_clear_count_break(Eterm mfa[3], int specified) { (BeamInstr) BeamOp(op_i_count_breakpoint)); } +int +erts_clear_time_break(Eterm mfa[3], int specified) { + ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0)); + return clear_break(mfa, specified, + (BeamInstr) BeamOp(op_i_time_breakpoint)); +} + int erts_clear_break(Eterm mfa[3], int specified) { ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0)); @@ -408,6 +420,31 @@ erts_is_count_break(BeamInstr *pc, Sint *count_ret) { return 0; } +int +erts_is_time_break(Uint *pc, Sint *count, Uint *s_time, Uint *us_time) { + BpDataTime *bdt = + (BpDataTime *) is_break(pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); + Uint i; + + if (bdt) { + if (count && s_time && us_time) { + *count = 0; + *s_time = 0; + *us_time = 0; + ErtsSmpBPLock(bdt); + for (i = 0; i < bdt->n; i++) { + *count += bdt->items[i].count; + *s_time += bdt->items[i].s_time; + *us_time += bdt->items[i].us_time; + } + ErtsSmpBPUnlock(bdt); + } + return !0; + } + return 0; +} + + BeamInstr * erts_find_local_func(Eterm mfa[3]) { Module *modp; @@ -432,6 +469,22 @@ erts_find_local_func(Eterm mfa[3]) { return NULL; } +void erts_do_time_break(Process *p, BpDataTime *bdt) { + Uint ms,s,u; + ErtsSchedulerData *esdp; + int ix = 0; + + esdp = erts_get_scheduler_data(); + ix = esdp->no - 1; + + get_sys_now(&ms,&s,&u); + //ErtsSmpBPLock(bdt); + // + bdt->items[ix].count++; + bdt->items[ix].s_time = 1; + bdt->items[ix].us_time = 1; + //ErtsSmpBPUnlock(bdt); +} /* ************************************************************************* @@ -519,6 +572,7 @@ static int set_function_break(Module *modp, BeamInstr *pc, if ( (bd = is_break(pc, break_op))) { if (break_op == (BeamInstr) BeamOp(op_i_trace_breakpoint) || break_op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) { + BpDataTrace *bdt = (BpDataTrace *) bd; Binary *old_match_spec; @@ -545,6 +599,18 @@ static int set_function_break(Module *modp, BeamInstr *pc, bdc->count = 0; /* Reset call counter */ } ErtsSmpBPUnlock(bdc); + } else if (break_op == (Uint) BeamOp(op_i_count_breakpoint)) { + BpDataTime *bdt = (BpDataTime *) bd; + Uint i = 0; + + ErtsSmpBPLock(bdt); + for (i = 0; i < bdt->n; i++) { + bdt->items[i].count = 0; + bdt->items[i].s_time = 0; + bdt->items[i].us_time = 0; + } + ErtsSmpBPUnlock(bdt); + } else { ASSERT (! count_op); } @@ -564,6 +630,12 @@ static int set_function_break(Module *modp, BeamInstr *pc, return 1; } size = sizeof(BpDataCount); + } else if (break_op == (BeamInstr) BeamOp(op_i_time_breakpoint)) { + if (count_op == erts_break_reset || count_op == erts_break_stop) { + /* Do not insert a new breakpoint */ + return 1; + } + size = sizeof(BpDataTime); } else { ASSERT(! count_op); ASSERT(break_op == (BeamInstr) BeamOp(op_i_debug_breakpoint)); @@ -576,6 +648,7 @@ static int set_function_break(Module *modp, BeamInstr *pc, ASSERT(*pc != (BeamInstr) BeamOp(op_i_mtrace_breakpoint)); ASSERT(*pc != (BeamInstr) BeamOp(op_i_debug_breakpoint)); ASSERT(*pc != (BeamInstr) BeamOp(op_i_count_breakpoint)); + ASSERT(*pc != (BeamInstr) BeamOp(op_i_time_breakpoint)); /* First breakpoint; create singleton ring */ bd = Alloc(size); BpInit(bd, *pc); @@ -585,6 +658,7 @@ static int set_function_break(Module *modp, BeamInstr *pc, ASSERT(*pc == (BeamInstr) BeamOp(op_i_trace_breakpoint) || *pc == (BeamInstr) BeamOp(op_i_mtrace_breakpoint) || *pc == (BeamInstr) BeamOp(op_i_debug_breakpoint) || + *pc == (BeamInstr) BeamOp(op_i_time_breakpoint) || *pc == (BeamInstr) BeamOp(op_i_count_breakpoint)); if (*pc == (BeamInstr) BeamOp(op_i_debug_breakpoint)) { /* Debug bp must be last, so if it is also first; @@ -617,6 +691,18 @@ static int set_function_break(Module *modp, BeamInstr *pc, MatchSetRef(match_spec); bdt->match_spec = match_spec; bdt->tracer_pid = tracer_pid; + } else if (break_op == (BeamInstr) BeamOp(op_i_time_breakpoint)) { + BpDataTime *bdt = (BpDataTime *) bd; + Uint i = 0; + + bdt->n = erts_no_schedulers; + bdt->items = Alloc(sizeof(bp_data_time_item_t)*(bdt->n)); + + for (i = 0; i < bdt->n; i++) { + bdt->items[i].count = 0; + bdt->items[i].s_time = 0; + bdt->items[i].us_time = 0; + } } else if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) { BpDataCount *bdc = (BpDataCount *) bd; @@ -739,6 +825,12 @@ static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { MatchSetUnref(bdt->match_spec); } + if (op == (BeamInstr) BeamOp(op_i_time_breakpoint)) { + BpDataTime *bdt = (BpDataTrace *) bd; + + Free(bdt->items); + bdt->n = 0; + } Free(bd); ASSERT(((BeamInstr) code_base[MI_NUM_BREAKPOINTS]) > 0); --(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]); diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index 786cbbe9d9..3fac85348f 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -75,6 +75,21 @@ typedef struct bp_data_count { /* Call count */ Sint count; } BpDataCount; +typedef struct { + Sint count; + Uint s_time; + Uint us_time; +} bp_data_time_item_t; + +typedef struct bp_data_time { /* Call time */ + struct bp_data *next; + struct bp_data *prev; + Uint orig_instr; + Uint n; + bp_data_time_item_t *items; +} BpDataTime; + + extern erts_smp_spinlock_t erts_bp_lock; #ifdef ERTS_SMP @@ -100,6 +115,27 @@ do { \ *(instr_result) = bdc->orig_instr; \ } while (0) +#define ErtsTimeBreak(pc,instr_result) \ +do { \ + BpDataTime *bdt = (BpDataTime *) (pc)[-4]; \ + Uint ms,s,u; \ + \ + ASSERT((pc)[-5] == (Uint) BeamOp(op_i_func_info_IaaI)); \ + ASSERT(bdt); \ + bdt = (BpDataTime *) bdt->next; \ + ASSERT(bdc); \ + (pc)[-4] = (Uint) bdt; \ + get_now(&ms,&s,&u); \ + ErtsSmpBPLock(bdt); \ + if (bdt->count >= 0) \ + bdt->count++; \ + bdt->s_time = 1; \ + bdt->us_time = 1; \ + ErtsSmpBPUnlock(bdt); \ + *(instr_result) = bdt->orig_instr; \ +} while (0) + + #define ErtsBreakSkip(pc,instr_result) \ do { \ BpData *bd = (BpData *) (pc)[-4]; \ @@ -140,6 +176,8 @@ int erts_set_debug_break(Eterm mfa[3], int specified); int erts_clear_debug_break(Eterm mfa[3], int specified); int erts_set_count_break(Eterm mfa[3], int specified, enum erts_break_op); int erts_clear_count_break(Eterm mfa[3], int specified); +int erts_set_time_break(Eterm mfa[3], int specified, enum erts_break_op); +int erts_clear_time_break(Eterm mfa[3], int specified); int erts_clear_break(Eterm mfa[3], int specified); @@ -159,6 +197,9 @@ int erts_is_mtrace_bif(BeamInstr *pc, Binary **match_spec_ret, Eterm *tracer_pid_ret); int erts_is_native_break(BeamInstr *pc); int erts_is_count_break(BeamInstr *pc, Sint *count_ret); +int erts_is_time_break(BeamInstr *pc, Sint *count, Uint *s_time, Uint *us_time); + +void erts_do_time_break(Process *p, BpDataTime *bdt); BeamInstr *erts_find_local_func(Eterm mfa[3]); diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index d42e74ccc9..145ae48590 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4408,6 +4408,22 @@ apply_bif_or_nif_epilogue: Goto(real_I); } + OpCase(i_time_breakpoint): { + BeamInstr real_I; + BpDataTime *bdt = (BpDataTime *) (I)[-4]; + + ASSERT((I)[-5] == (Uint) BeamOp(op_i_func_info_IaaI)); + ASSERT(bdt); + bdt = (BpDataTime *) bdt->next; + ASSERT(bdt); + (I)[-4] = (Uint) bdt; + real_I = bdt->orig_instr; + erts_do_time_break(c_p, bdt); + + ASSERT(VALID_INSTR(real_I)); + Goto(real_I); + } + OpCase(i_trace_breakpoint): if (! IS_TRACED_FL(c_p, F_TRACE_CALLS)) { BeamInstr real_I; diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 7fe4410e0d..20a58e5ed9 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -186,6 +186,14 @@ trace_pattern_3(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) flags.breakpoint = 1; flags.call_count = 1; break; + case am_call_time: + if (is_global) { + goto error; + } + flags.breakpoint = 1; + flags.call_time = 1; + break; + default: goto error; } @@ -235,6 +243,8 @@ trace_pattern_3(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) |= flags.meta; erts_default_trace_pattern_flags.call_count |= (on == 1) ? flags.call_count : 0; + erts_default_trace_pattern_flags.call_time + |= (on == 1) ? flags.call_time : 0; } else { erts_default_trace_pattern_flags.local &= ~flags.local; @@ -242,10 +252,13 @@ trace_pattern_3(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) &= ~flags.meta; erts_default_trace_pattern_flags.call_count &= ~flags.call_count; + erts_default_trace_pattern_flags.call_time + &= ~flags.call_time; if (! (erts_default_trace_pattern_flags.breakpoint = erts_default_trace_pattern_flags.local | erts_default_trace_pattern_flags.meta | - erts_default_trace_pattern_flags.call_count)) { + erts_default_trace_pattern_flags.call_count | + erts_default_trace_pattern_flags.call_time)) { erts_default_trace_pattern_is_on = !!on; /* i.e off */ } } @@ -267,8 +280,9 @@ trace_pattern_3(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) if (on) { if (on != 1) { flags.call_count = 0; + flags.call_time = 0; } - flags.breakpoint = flags.local | flags.meta | flags.call_count; + flags.breakpoint = flags.local | flags.meta | flags.call_count | flags.call_time; erts_default_trace_pattern_flags = flags; /* Struct copy */ erts_default_trace_pattern_is_on = !!flags.breakpoint; } @@ -352,7 +366,6 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on, - Uint erts_trace_flag2bit(Eterm flag) { @@ -380,7 +393,7 @@ erts_trace_flag2bit(Eterm flag) default: return 0; } } - + /* Scan the argument list and sort out the trace flags. ** ** Returns !0 on success, 0 on failure. @@ -931,6 +944,7 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key) #define FUNC_TRACE_LOCAL_TRACE (1<<2) #define FUNC_TRACE_META_TRACE (1<<3) #define FUNC_TRACE_COUNT_TRACE (1<<4) +#define FUNC_TRACE_TIME_TRACE (1<<5) /* * Returns either FUNC_TRACE_NOEXIST, FUNC_TRACE_UNTRACED, * FUNC_TRACE_GLOBAL_TRACE, or, @@ -946,10 +960,12 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key) * If the return value contains FUNC_TRACE_COUNT_TRACE, *count is set. */ static int function_is_traced(Eterm mfa[3], - Binary **ms, /* out */ - Binary **ms_meta, /* out */ + Binary **ms, /* out */ + Binary **ms_meta, /* out */ Eterm *tracer_pid_meta, /* out */ - Sint *count) /* out */ + Sint *count, /* out */ + Uint *s_time, /* out */ + Uint *us_time) /* out */ { Export e; Export* ep; @@ -1001,7 +1017,9 @@ static int function_is_traced(Eterm mfa[3], | (erts_is_mtrace_break(code, ms_meta, tracer_pid_meta) ? FUNC_TRACE_META_TRACE : 0) | (erts_is_count_break(code, count) - ? FUNC_TRACE_COUNT_TRACE : 0); + ? FUNC_TRACE_COUNT_TRACE : 0) + | (erts_is_time_break(code, count, s_time, us_time) + ? FUNC_TRACE_TIME_TRACE : 0); return r ? r : FUNC_TRACE_UNTRACED; } @@ -1016,6 +1034,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) DeclareTmpHeap(mfa,3,p); /* Not really heap here, but might be when setting pattern */ Binary *ms = NULL, *ms_meta = NULL; Sint count = 0; + Uint s_time = 0, us_time = 0; Eterm traced = am_false; Eterm match_spec = am_false; Eterm retval = am_false; @@ -1039,7 +1058,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) mfa[1] = tp[2]; mfa[2] = signed_val(tp[3]); - r = function_is_traced(mfa, &ms, &ms_meta, &meta, &count); + r = function_is_traced(mfa, &ms, &ms_meta, &meta, &count, &s_time, &us_time); switch (r) { case FUNC_TRACE_NOEXIST: UnUseTmpHeap(3,p); @@ -1092,8 +1111,17 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) erts_make_integer(count, p); } break; + case am_call_time: + if (r & FUNC_TRACE_TIME_TRACE) { + hp = HAlloc(p, 4); + retval = TUPLE3(hp, + erts_make_integer(count, p), + erts_make_integer(s_time, p), + erts_make_integer(us_time,p)); hp += 4; + } + break; case am_all: { - Eterm match_spec_meta = am_false, c = am_false, t; + Eterm match_spec_meta = am_false, c = am_false, t, ct; if (ms) { match_spec = MatchSetGetSource(ms); @@ -1111,10 +1139,16 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) erts_make_integer(-count-1, p) : erts_make_integer(count, p); } - hp = HAlloc(p, (3+2)*5); + if (r & FUNC_TRACE_TIME_TRACE) { + hp = HAlloc(p, 4); + ct = TUPLE3(hp, erts_make_integer(count, p), erts_make_integer(s_time, p), erts_make_integer(us_time,p)); hp += 4; + } + hp = HAlloc(p, (3+2)*6); retval = NIL; t = TUPLE2(hp, am_call_count, c); hp += 3; retval = CONS(hp, t, retval); hp += 2; + t = TUPLE2(hp, am_call_time, ct); hp += 3; + retval = CONS(hp, t, retval); hp += 2; t = TUPLE2(hp, am_meta_match_spec, match_spec_meta); hp += 3; retval = CONS(hp, t, retval); hp += 2; t = TUPLE2(hp, am_meta, meta); hp += 3; @@ -1210,6 +1244,13 @@ trace_info_on_load(Process* p, Eterm key) } else { return TUPLE2(hp, key, am_false); } + case am_call_time: + hp = HAlloc(p, 3); + if (erts_default_trace_pattern_flags.call_time) { + return TUPLE2(hp, key, am_true); + } else { + return TUPLE2(hp, key, am_false); + } case am_all: { Eterm match_spec = am_false, meta_match_spec = am_false, r = NIL, t; @@ -1284,6 +1325,7 @@ erts_set_trace_pattern(Eterm* mfa, int specified, for (j = 0; j < specified && mfa[j] == ep->code[j]; j++) { /* Empty loop body */ } + if (j == specified) { if (on) { if (! flags.breakpoint) @@ -1405,6 +1447,9 @@ erts_set_trace_pattern(Eterm* mfa, int specified, if (flags.call_count) { m = erts_set_count_break(mfa, specified, on); } + if (flags.call_time) { + m = erts_set_time_break(mfa, specified, on); + } /* All assignments to 'm' above should give the same value, * so just use the last */ matches += m; @@ -1420,6 +1465,9 @@ erts_set_trace_pattern(Eterm* mfa, int specified, if (flags.call_count) { m = erts_clear_count_break(mfa, specified); } + if (flags.call_count) { + m = erts_clear_time_break(mfa, specified); + } /* All assignments to 'm' above should give the same value, * so just use the last */ matches += m; diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index e2cb523cf5..bacaa25d23 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -397,8 +397,9 @@ extern ErtsSchedulerData *erts_scheduler_data; #define ERTS_PSD_SAVED_CALLS_BUF 1 #define ERTS_PSD_SCHED_ID 2 #define ERTS_PSD_DIST_ENTRY 3 +#define ERTS_PSD_CALL_TIME_BP 4 -#define ERTS_PSD_SIZE 4 +#define ERTS_PSD_SIZE 5 typedef struct { void *data[ERTS_PSD_SIZE]; @@ -1208,6 +1209,12 @@ erts_psd_set(Process *p, ErtsProcLocks plocks, int ix, void *data) #define ERTS_PROC_SET_SAVED_CALLS_BUF(P, L, SCB) \ ((struct saved_calls *) erts_psd_set((P), (L), ERTS_PSD_SAVED_CALLS_BUF, (void *) (SCB))) +#define ERTS_PROC_GET_CALL_TIME(P) \ + ((process_breakpoint_time_t *) erts_psd_get((P), ERTS_PSD_CALL_TIME_BP)) +#define ERTS_PROC_SET_CALL_TIME(P, L, PBT) \ + ((process_breakpoint_time_t *) erts_psd_set((P), (L), ERTS_PSD_CALL_TIME_BP, (void *) (PBT))) + + ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p); ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p, ErtsProcLocks plocks, diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 19dd3d6f97..711b613028 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1741,6 +1741,7 @@ struct trace_pattern_flags { unsigned int local : 1; /* Local call trace breakpoint */ unsigned int meta : 1; /* Metadata trace breakpoint */ unsigned int call_count : 1; /* Fast call count breakpoint */ + unsigned int call_time : 1; /* Fast call time breakpoint */ }; extern const struct trace_pattern_flags erts_trace_pattern_flags_off; int erts_set_trace_pattern(Eterm* mfa, int specified, diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 42c84989c6..32abdccd7b 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -84,6 +84,7 @@ i_trace_breakpoint i_mtrace_breakpoint i_debug_breakpoint i_count_breakpoint +i_time_breakpoint i_return_to_trace i_yield i_global_cons -- cgit v1.2.3 From cfa905aece83e5a07f67fa169fe8f44c1c163bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 20 Apr 2010 15:44:07 +0200 Subject: Fix lock-checking for PSD call_time breakpoints --- erts/emulator/beam/erl_process.c | 7 ++++++- erts/emulator/beam/erl_process.h | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 055733ac49..4befa0aca9 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -392,7 +392,12 @@ erts_pre_init_process(void) erts_psd_required_locks[ERTS_PSD_DIST_ENTRY].get_locks = ERTS_PSD_DIST_ENTRY_GET_LOCKS; erts_psd_required_locks[ERTS_PSD_DIST_ENTRY].set_locks - = ERTS_PSD_DIST_ENTRY_GET_LOCKS; + = ERTS_PSD_DIST_ENTRY_SET_LOCKS; + + erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].get_locks + = ERTS_PSD_CALL_TIME_BP_GET_LOCKS; + erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].set_locks + = ERTS_PSD_CALL_TIME_BP_SET_LOCKS; /* Check that we have locks for all entries */ for (ix = 0; ix < ERTS_PSD_SIZE; ix++) { diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index bacaa25d23..8f9f7f004e 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -420,6 +420,9 @@ typedef struct { #define ERTS_PSD_DIST_ENTRY_GET_LOCKS ERTS_PROC_LOCK_MAIN #define ERTS_PSD_DIST_ENTRY_SET_LOCKS ERTS_PROC_LOCK_MAIN +#define ERTS_PSD_CALL_TIME_BP_GET_LOCKS ERTS_PROC_LOCK_MAIN +#define ERTS_PSD_CALL_TIME_BP_SET_LOCKS ERTS_PROC_LOCK_MAIN + typedef struct { ErtsProcLocks get_locks; ErtsProcLocks set_locks; -- cgit v1.2.3 From 78d5145d68783dca4aa43bfd38fb9afecc59c416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 21 Apr 2010 15:29:31 +0200 Subject: Add processes hashes for call time breakpoints Call time breakpoint tracing traces per call trace per process. - Add hashes to support the extra dimension. - Teach trace_info/2 to handle the extra information dimension. --- erts/emulator/beam/beam_bp.c | 289 ++++++++++++++++++++++++++++++++----- erts/emulator/beam/beam_bp.h | 25 +++- erts/emulator/beam/erl_bif_trace.c | 23 ++- 3 files changed, 278 insertions(+), 59 deletions(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index a78645a9c7..f264ebfa3f 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -30,6 +30,7 @@ #include "error.h" #include "erl_binary.h" #include "beam_bp.h" +#include "erl_term.h" /* ************************************************************************* ** Macros @@ -127,6 +128,23 @@ static int clear_function_break(Module *modp, BeamInstr *pc, static BpData *is_break(BeamInstr *pc, BeamInstr break_op); +/* bp_hash */ +#define BP_TIME_ADD(pi0, pi1) \ + do { \ + Uint r; \ + (pi0)->count += (pi1)->count; \ + (pi0)->s_time += (pi1)->s_time; \ + (pi0)->us_time += (pi1)->us_time; \ + r = (pi0)->us_time / 1000000; \ + (pi0)->s_time += r; \ + (pi0)->us_time = (pi0)->us_time % 1000000; \ + } while(0) + +static void bp_hash_init(bp_time_hash_t *hash, Uint n); +static void bp_hash_rehash(bp_time_hash_t *hash, Uint n); +static ERTS_INLINE bp_data_time_item_t * bp_hash_get(bp_time_hash_t *hash, bp_data_time_item_t *sitem); +static ERTS_INLINE bp_data_time_item_t * bp_hash_put(bp_time_hash_t *hash, bp_data_time_item_t *sitem); +static void bp_hash_delete(bp_time_hash_t *hash); /* ************************************************************************* @@ -420,27 +438,58 @@ erts_is_count_break(BeamInstr *pc, Sint *count_ret) { return 0; } -int -erts_is_time_break(Uint *pc, Sint *count, Uint *s_time, Uint *us_time) { - BpDataTime *bdt = - (BpDataTime *) is_break(pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); - Uint i; +int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *retval) { + BpDataTime *bdt = (BpDataTime *) is_break(pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); + Uint i, ix; + bp_time_hash_t hash; + Uint size; + Eterm *hp, t; + bp_data_time_item_t *item = NULL; if (bdt) { - if (count && s_time && us_time) { - *count = 0; - *s_time = 0; - *us_time = 0; - ErtsSmpBPLock(bdt); + if (retval) { + /* collect all hashes to one hash */ + bp_hash_init(&hash, 64); + /* foreach threadspecific hash */ for (i = 0; i < bdt->n; i++) { - *count += bdt->items[i].count; - *s_time += bdt->items[i].s_time; - *us_time += bdt->items[i].us_time; + bp_data_time_item_t *sitem; + + /* foreach hash bucket not NIL*/ + for(ix = 0; ix < bdt->hash[i].n; ix++) { + item = &(bdt->hash[i].item[ix]); + if (item->pid != NIL) { + sitem = bp_hash_get(&hash, item); + if (sitem) { + BP_TIME_ADD(sitem, item); + } else { + bp_hash_put(&hash, item); + } + } + } } - ErtsSmpBPUnlock(bdt); + + *retval = NIL; + if (hash.used > 0) { + size = (5 + 2)*hash.used; + hp = HAlloc(p, size); + + for(ix = 0; ix < hash.n; ix++) { + item = &(hash.item[ix]); + if (item->pid != NIL) { + t = TUPLE4(hp, item->pid, + make_small(item->count), + make_small(item->s_time), + make_small(item->us_time)); + hp += 5; + *retval = CONS(hp, t, *retval); hp += 2; + } + } + } + bp_hash_delete(&hash); } return !0; } + return 0; } @@ -469,21 +518,182 @@ erts_find_local_func(Eterm mfa[3]) { return NULL; } +/* bp_hash */ + +static void bp_hash_init(bp_time_hash_t *hash, Uint n) { + Uint size = sizeof(bp_data_time_item_t)*n; + Uint i; + + hash->n = n; + hash->used = 0; + + + hash->item = (bp_data_time_item_t *)Alloc(size); + sys_memzero(hash->item, size); + + for(i = 0; i < n; ++i) { + hash->item[i].pid = NIL; + } +} + +static void bp_hash_rehash(bp_time_hash_t *hash, Uint n) { + bp_data_time_item_t *item = NULL; + Uint size = sizeof(bp_data_time_item_t)*n; + Uint ix; + Uint hval; + + item = (bp_data_time_item_t *)Alloc(size); + sys_memzero(item, size); + + for( ix = 0; ix < n; ++ix) { + item[ix].pid = NIL; + } + + for( ix = 0; ix < hash->n; ix++) { + if (hash->item[ix].pid != NIL) { + + hval = ((hash->item[ix].pid) >> 4) % n; /* new n */ + + while (item[hval].pid != NIL) { + hval = (hval + 1) % n; + } + item[hval].pid = hash->item[ix].pid; + item[hval].count = hash->item[ix].count; + item[hval].s_time = hash->item[ix].s_time; + item[hval].us_time = hash->item[ix].us_time; + } + } + + Free(hash->item); + hash->n = n; + hash->item = item; +} +static ERTS_INLINE bp_data_time_item_t * bp_hash_get(bp_time_hash_t *hash, bp_data_time_item_t *sitem) { + Eterm pid = sitem->pid; + Uint hval = (pid >> 4) % hash->n; + bp_data_time_item_t *item = NULL; + + item = hash->item; + + while (item[hval].pid != pid) { + if (item[hval].pid == NIL) return NULL; + hval = (hval + 1) % hash->n; + } + + return &(item[hval]); +} + +static ERTS_INLINE bp_data_time_item_t * bp_hash_put(bp_time_hash_t *hash, bp_data_time_item_t* sitem) { + Uint hval = (sitem->pid >> 4) % hash->n; + float r = 0.0; + bp_data_time_item_t *item; + + /* make sure that the hash is not saturated */ + /* if saturated, rehash it */ + + r = hash->used / (float) hash->n; + + if (r > 0.7f) { + bp_hash_rehash(hash, hash->n * 2); + } + + /* find free slot */ + item = hash->item; + + while (item[hval].pid != NIL) { + hval = (hval + 1) % hash->n; + } + item = &(hash->item[hval]); + + item->pid = sitem->pid; + item->s_time = sitem->s_time; + item->us_time = sitem->us_time; + item->count = sitem->count; + hash->used++; + + return item; +} + +static void bp_hash_delete(bp_time_hash_t *hash) { + hash->n = 0; + hash->used = 0; + Free(hash->item); + hash->item = NULL; +} + +static void bp_time_diff(bp_data_time_item_t *item, /* out */ + process_breakpoint_time_t *pbt, /* in */ + Uint ms, Uint s, Uint us) { + int dms,ds,dus; + + dms = ms - pbt->ms; + ds = s - pbt->s; + dus = us - pbt->us; + + if (dms > 0) { + item->s_time = (ds + dms * 1000000); + } else { + item->s_time = ds; + } + if ( dus < 0 ) { + item->us_time = (dus + 1000000); + item->s_time -= 1; + } else { + item->us_time = dus; + } +} + void erts_do_time_break(Process *p, BpDataTime *bdt) { - Uint ms,s,u; - ErtsSchedulerData *esdp; - int ix = 0; + Uint ms,s,us; + process_breakpoint_time_t *pbt = NULL; + int ix = 0; - esdp = erts_get_scheduler_data(); - ix = esdp->no - 1; + /* get previous timestamp and breakpoint + * from the process' psd */ + pbt = ERTS_PROC_GET_CALL_TIME(p); + get_sys_now(&ms,&s,&us); - get_sys_now(&ms,&s,&u); - //ErtsSmpBPLock(bdt); - // - bdt->items[ix].count++; - bdt->items[ix].s_time = 1; - bdt->items[ix].us_time = 1; - //ErtsSmpBPUnlock(bdt); + if (pbt) { + bp_data_time_item_t sitem, *item; + bp_time_hash_t *h; + +#ifdef ERTS_SMP + ix = p->scheduler_data->no - 1; +#else + ix = 0; +#endif + bp_time_diff(&sitem, pbt, ms, s, us); + sitem.pid = p->id; + sitem.count = 1; + + h = &(pbt->bdt->hash[ix]); + + item = bp_hash_get(h, &sitem); + if (!item) { + bp_hash_put(h, &sitem); + } else { + BP_TIME_ADD(item, &sitem); + } + + pbt->bdt = bdt; + pbt->ms = ms; + pbt->s = s; + pbt->us = us; + + } else { + pbt = Alloc(sizeof(process_breakpoint_time_t)); + + pbt->bdt = bdt; + pbt->ms = ms; + pbt->s = s; + pbt->us = us; + + (void *) ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCK_MAIN, pbt); + } + + /* timestamp start of this call + * save ms, s, us, and bdt to the process. + */ } @@ -603,13 +813,12 @@ static int set_function_break(Module *modp, BeamInstr *pc, BpDataTime *bdt = (BpDataTime *) bd; Uint i = 0; - ErtsSmpBPLock(bdt); + /*XXX: must block system */ + for (i = 0; i < bdt->n; i++) { - bdt->items[i].count = 0; - bdt->items[i].s_time = 0; - bdt->items[i].us_time = 0; + bp_hash_delete(&(bdt->hash[i])); + bp_hash_init(&(bdt->hash[i]), 32); } - ErtsSmpBPUnlock(bdt); } else { ASSERT (! count_op); @@ -695,13 +904,11 @@ static int set_function_break(Module *modp, BeamInstr *pc, BpDataTime *bdt = (BpDataTime *) bd; Uint i = 0; - bdt->n = erts_no_schedulers; - bdt->items = Alloc(sizeof(bp_data_time_item_t)*(bdt->n)); + bdt->n = erts_no_schedulers; + bdt->hash = Alloc(sizeof(bp_time_hash_t)*(bdt->n)); for (i = 0; i < bdt->n; i++) { - bdt->items[i].count = 0; - bdt->items[i].s_time = 0; - bdt->items[i].us_time = 0; + bp_hash_init(&(bdt->hash[i]), 32); } } else if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) { BpDataCount *bdc = (BpDataCount *) bd; @@ -822,13 +1029,15 @@ static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) { BpDataTrace *bdt = (BpDataTrace *) bd; - MatchSetUnref(bdt->match_spec); } if (op == (BeamInstr) BeamOp(op_i_time_breakpoint)) { - BpDataTime *bdt = (BpDataTrace *) bd; - - Free(bdt->items); + BpDataTime *bdt = (BpDataTime *) bd; + Uint i = 0; + for( i = 0; i < bdt->n; ++i) { + bp_hash_delete(&(bdt->hash[i])); + } + Free(bdt->hash); bdt->n = 0; } Free(bd); diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index 3fac85348f..58fd65399d 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -24,6 +24,7 @@ #include "sys.h" #include "erl_vm.h" #include "global.h" +//#include "hash.h" @@ -76,22 +77,36 @@ typedef struct bp_data_count { /* Call count */ } BpDataCount; typedef struct { - Sint count; - Uint s_time; - Uint us_time; + Eterm pid; + Sint count; + Uint s_time; + Uint us_time; } bp_data_time_item_t; +typedef struct { + Uint n; + Uint used; + bp_data_time_item_t *item; +} bp_time_hash_t; + typedef struct bp_data_time { /* Call time */ struct bp_data *next; struct bp_data *prev; Uint orig_instr; Uint n; - bp_data_time_item_t *items; + bp_time_hash_t *hash; } BpDataTime; +typedef struct { + Uint ms; + Uint s; + Uint us; + BpDataTime *bdt; +} process_breakpoint_time_t; /* used within psd */ extern erts_smp_spinlock_t erts_bp_lock; + #ifdef ERTS_SMP #define ErtsSmpBPLock(BDC) erts_smp_spin_lock(&erts_bp_lock) #define ErtsSmpBPUnlock(BDC) erts_smp_spin_unlock(&erts_bp_lock) @@ -197,7 +212,7 @@ int erts_is_mtrace_bif(BeamInstr *pc, Binary **match_spec_ret, Eterm *tracer_pid_ret); int erts_is_native_break(BeamInstr *pc); int erts_is_count_break(BeamInstr *pc, Sint *count_ret); -int erts_is_time_break(BeamInstr *pc, Sint *count, Uint *s_time, Uint *us_time); +int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *call_time); void erts_do_time_break(Process *p, BpDataTime *bdt); diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 20a58e5ed9..26390d397c 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -959,13 +959,13 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key) * * If the return value contains FUNC_TRACE_COUNT_TRACE, *count is set. */ -static int function_is_traced(Eterm mfa[3], +static int function_is_traced(Process *p, + Eterm mfa[3], Binary **ms, /* out */ Binary **ms_meta, /* out */ Eterm *tracer_pid_meta, /* out */ Sint *count, /* out */ - Uint *s_time, /* out */ - Uint *us_time) /* out */ + Eterm *call_time) /* out */ { Export e; Export* ep; @@ -1018,7 +1018,7 @@ static int function_is_traced(Eterm mfa[3], ? FUNC_TRACE_META_TRACE : 0) | (erts_is_count_break(code, count) ? FUNC_TRACE_COUNT_TRACE : 0) - | (erts_is_time_break(code, count, s_time, us_time) + | (erts_is_time_break(p, code, call_time) ? FUNC_TRACE_TIME_TRACE : 0); return r ? r : FUNC_TRACE_UNTRACED; @@ -1034,11 +1034,11 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) DeclareTmpHeap(mfa,3,p); /* Not really heap here, but might be when setting pattern */ Binary *ms = NULL, *ms_meta = NULL; Sint count = 0; - Uint s_time = 0, us_time = 0; Eterm traced = am_false; Eterm match_spec = am_false; Eterm retval = am_false; Eterm meta = am_false; + Eterm call_time; int r; @@ -1058,7 +1058,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) mfa[1] = tp[2]; mfa[2] = signed_val(tp[3]); - r = function_is_traced(mfa, &ms, &ms_meta, &meta, &count, &s_time, &us_time); + r = function_is_traced(p, mfa, &ms, &ms_meta, &meta, &count, &call_time); switch (r) { case FUNC_TRACE_NOEXIST: UnUseTmpHeap(3,p); @@ -1113,15 +1113,11 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) break; case am_call_time: if (r & FUNC_TRACE_TIME_TRACE) { - hp = HAlloc(p, 4); - retval = TUPLE3(hp, - erts_make_integer(count, p), - erts_make_integer(s_time, p), - erts_make_integer(us_time,p)); hp += 4; + retval = call_time; } break; case am_all: { - Eterm match_spec_meta = am_false, c = am_false, t, ct; + Eterm match_spec_meta = am_false, c = am_false, t, ct = am_false; if (ms) { match_spec = MatchSetGetSource(ms); @@ -1140,8 +1136,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) erts_make_integer(count, p); } if (r & FUNC_TRACE_TIME_TRACE) { - hp = HAlloc(p, 4); - ct = TUPLE3(hp, erts_make_integer(count, p), erts_make_integer(s_time, p), erts_make_integer(us_time,p)); hp += 4; + ct = call_time; } hp = HAlloc(p, (3+2)*6); retval = NIL; -- cgit v1.2.3 From 35fc33a60608c160f20387840fa6317b09d05d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 21 Apr 2010 16:09:14 +0200 Subject: Fix set_function_break to use correct breakpoint op_i_time_breakpoint is now used --- erts/emulator/beam/beam_bp.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index f264ebfa3f..7c85885fa2 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -129,14 +129,14 @@ static int clear_function_break(Module *modp, BeamInstr *pc, static BpData *is_break(BeamInstr *pc, BeamInstr break_op); /* bp_hash */ -#define BP_TIME_ADD(pi0, pi1) \ - do { \ - Uint r; \ - (pi0)->count += (pi1)->count; \ - (pi0)->s_time += (pi1)->s_time; \ - (pi0)->us_time += (pi1)->us_time; \ - r = (pi0)->us_time / 1000000; \ - (pi0)->s_time += r; \ +#define BP_TIME_ADD(pi0, pi1) \ + do { \ + Uint r; \ + (pi0)->count += (pi1)->count; \ + (pi0)->s_time += (pi1)->s_time; \ + (pi0)->us_time += (pi1)->us_time; \ + r = (pi0)->us_time / 1000000; \ + (pi0)->s_time += r; \ (pi0)->us_time = (pi0)->us_time % 1000000; \ } while(0) @@ -809,7 +809,7 @@ static int set_function_break(Module *modp, BeamInstr *pc, bdc->count = 0; /* Reset call counter */ } ErtsSmpBPUnlock(bdc); - } else if (break_op == (Uint) BeamOp(op_i_count_breakpoint)) { + } else if (break_op == (Uint) BeamOp(op_i_time_breakpoint)) { BpDataTime *bdt = (BpDataTime *) bd; Uint i = 0; -- cgit v1.2.3 From 81a49dd8768c1a456691e0b047a4dda18aa258e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 29 Apr 2010 13:46:48 +0200 Subject: Remove trailing character in beam_bif_load --- erts/emulator/beam/beam_bif_load.c | 1 - 1 file changed, 1 deletion(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 8462f1c7fd..596ad9a010 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -368,7 +368,6 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) BIF_RET(am_true); } - static void set_default_trace_pattern(Eterm module) { -- cgit v1.2.3 From 891682094bf1db038cdf9addffe0b107f40ab6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 29 Apr 2010 14:52:18 +0200 Subject: Add return_time trace and PSD deletions --- erts/emulator/beam/beam_bp.c | 287 +++++++++++++++++++++++++++++++------ erts/emulator/beam/beam_bp.h | 12 +- erts/emulator/beam/beam_emu.c | 69 +++++++-- erts/emulator/beam/erl_bif_trace.c | 22 ++- erts/emulator/beam/erl_process.c | 25 +++- erts/emulator/beam/global.h | 1 + 6 files changed, 350 insertions(+), 66 deletions(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 7c85885fa2..48e7b6675a 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -630,70 +630,233 @@ static void bp_time_diff(bp_data_time_item_t *item, /* out */ ds = s - pbt->s; dus = us - pbt->us; - if (dms > 0) { - item->s_time = (ds + dms * 1000000); - } else { - item->s_time = ds; + /* get_sys_now may return zero difftime, + * this is ok. + */ + + ASSERT(dms >= 0 || ds >= 0 || dus >= 0); + + if (dus < 0) { + dus += 1000000; + ds -= 1; } - if ( dus < 0 ) { - item->us_time = (dus + 1000000); - item->s_time -= 1; - } else { - item->us_time = dus; + if (ds < 0) { + ds += 1000000; } + + item->s_time = ds; + item->us_time = dus; } -void erts_do_time_break(Process *p, BpDataTime *bdt) { +void erts_schedule_time_break(Process *p, Uint schedule) { + Uint ms, s, us; + process_breakpoint_time_t *pbt = NULL; + Uint ix = 0; + bp_data_time_item_t sitem, *item = NULL; + bp_time_hash_t *h = NULL; + + ASSERT(p); + + pbt = ERTS_PROC_GET_CALL_TIME(p); + + ASSERT( (p->status == P_RUNNING) || + (p->status == P_WAITING) || + (p->status == P_RUNABLE)); + + if (pbt) { + get_sys_now(&ms,&s,&us); + + switch(schedule) { + case ERTS_BP_CALL_TIME_SCHEDULE_EXITING : + case ERTS_BP_CALL_TIME_SCHEDULE_OUT : + /* When a process is scheduled _out_, + * timestamp it and add its delta to + * the previous breakpoint. + */ + +#ifdef ERTS_SMP + ix = p->scheduler_data->no - 1; +#else + ix = 0; +#endif + bp_time_diff(&sitem, pbt, ms, s, us); + sitem.pid = p->id; + sitem.count = 1; + /*count is set to 0 if out and 1 if exiting */ + + h = &(pbt->bdt->hash[ix]); + + ASSERT(h->item); + + item = bp_hash_get(h, &sitem); + if (!item) { + item = bp_hash_put(h, &sitem); + } else { + BP_TIME_ADD(item, &sitem); + } + break; + case ERTS_BP_CALL_TIME_SCHEDULE_IN : + /* When a process is scheduled _in_, + * timestamp it and remove the previous + * timestamp in the psd. + */ + sitem.pid = p->id; + sitem.count = -1; + sitem.us_time = 0; + sitem.s_time = 0; + + h = &(pbt->bdt->hash[ix]); + + ASSERT(h->item); + + item = bp_hash_get(h, &sitem); + if (!item) { + item = bp_hash_put(h, &sitem); + } else { + BP_TIME_ADD(item, &sitem); + } + pbt->ms = ms; + pbt->s = s; + pbt->us = us; + break; + default : + ASSERT(0); + /* will never happen */ + break; + } +#ifdef DEBUG + } else { + /* if pbt is null, then the process has just been spawned + * and status should be runnable. + */ + ASSERT( (p->status == P_RUNABLE) || + (p->status == P_WAITING)); +#endif + } /* pbt */ +} + +/* call_time breakpoint + * Accumulated times are added to the previous bp, + * not the current one. The current one is saved + * for future reference. + * The previous breakpoint is stored in the process it self, the psd. + * We do not need to store in a stack frame. + * There is no need for locking, each thread has its own + * area in each bp to save data. + * Since we need to diffrentiate between processes for each bp, + * every bp has a hash (per thread) to process-bp statistics. + * - egil + */ + +void erts_do_time_break(Process *p, BpDataTime *bdt, Uint type) { Uint ms,s,us; process_breakpoint_time_t *pbt = NULL; int ix = 0; + bp_data_time_item_t sitem, *item = NULL; + bp_time_hash_t *h = NULL; + + ASSERT(p); + ASSERT(bdt); + ASSERT(p->status == P_RUNNING); /* get previous timestamp and breakpoint - * from the process' psd */ + * from the process psd */ + pbt = ERTS_PROC_GET_CALL_TIME(p); get_sys_now(&ms,&s,&us); - if (pbt) { - bp_data_time_item_t sitem, *item; - bp_time_hash_t *h; + switch(type) { + case ERTS_BP_CALL_TIME_CALL: + /* get pbt + * timestamp = t0 + * set ts0 to pbt + * add call count here? + */ + if (!pbt) { + pbt = Alloc(sizeof(process_breakpoint_time_t)); + (void *) ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCK_MAIN, pbt); + } + + pbt->bdt = bdt; /* needed for schedule? */ + + pbt->ms = ms; + pbt->s = s; + pbt->us = us; + + + break; + case ERTS_BP_CALL_TIME_TAIL_CALL: + + ASSERT(pbt); #ifdef ERTS_SMP - ix = p->scheduler_data->no - 1; + ix = p->scheduler_data->no - 1; #else - ix = 0; + ix = 0; #endif - bp_time_diff(&sitem, pbt, ms, s, us); - sitem.pid = p->id; - sitem.count = 1; + bp_time_diff(&sitem, pbt, ms, s, us); + sitem.pid = p->id; + sitem.count = 1; - h = &(pbt->bdt->hash[ix]); + h = &(bdt->hash[ix]); - item = bp_hash_get(h, &sitem); - if (!item) { - bp_hash_put(h, &sitem); - } else { - BP_TIME_ADD(item, &sitem); - } + ASSERT(h->item); - pbt->bdt = bdt; - pbt->ms = ms; - pbt->s = s; - pbt->us = us; + item = bp_hash_get(h, &sitem); + if (!item) { + item = bp_hash_put(h, &sitem); + } else { + BP_TIME_ADD(item, &sitem); + } - } else { - pbt = Alloc(sizeof(process_breakpoint_time_t)); - pbt->bdt = bdt; - pbt->ms = ms; - pbt->s = s; - pbt->us = us; + pbt->bdt = bdt; /* needed for schedule? */ - (void *) ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCK_MAIN, pbt); - } + pbt->ms = ms; + pbt->s = s; + pbt->us = us; + break; - /* timestamp start of this call - * save ms, s, us, and bdt to the process. - */ + + case ERTS_BP_CALL_TIME_RETURN: + /* get pbt + * timestamp = t1 + * get ts0 from pbt + * get item from bdt->hash[bp_hash(p->id)] + * ack diff (t1, t0) to item + */ + + if(pbt) { + /* might have been removed due to + * trace_pattern(false) + */ +#ifdef ERTS_SMP + ix = p->scheduler_data->no - 1; +#else + ix = 0; +#endif + bp_time_diff(&sitem, pbt, ms, s, us); + sitem.pid = p->id; + sitem.count = 1; + + h = &(bdt->hash[ix]); + + ASSERT(h->item); + + item = bp_hash_get(h, &sitem); + if (!item) { + item = bp_hash_put(h, &sitem); + } else { + BP_TIME_ADD(item, &sitem); + } + + pbt->bdt = NULL; + pbt->ms = ms; + pbt->s = s; + pbt->us = us; + } + break; + } } @@ -814,10 +977,14 @@ static int set_function_break(Module *modp, BeamInstr *pc, Uint i = 0; /*XXX: must block system */ - - for (i = 0; i < bdt->n; i++) { - bp_hash_delete(&(bdt->hash[i])); - bp_hash_init(&(bdt->hash[i]), 32); + if (count_op == erts_break_stop) { + bdt->pause = 1; + } else { + bdt->pause = 0; + for (i = 0; i < bdt->n; i++) { + bp_hash_delete(&(bdt->hash[i])); + bp_hash_init(&(bdt->hash[i]), 32); + } } } else { @@ -904,8 +1071,9 @@ static int set_function_break(Module *modp, BeamInstr *pc, BpDataTime *bdt = (BpDataTime *) bd; Uint i = 0; - bdt->n = erts_no_schedulers; - bdt->hash = Alloc(sizeof(bp_time_hash_t)*(bdt->n)); + bdt->pause = 0; + bdt->n = erts_no_schedulers; + bdt->hash = Alloc(sizeof(bp_time_hash_t)*(bdt->n)); for (i = 0; i < bdt->n; i++) { bp_hash_init(&(bdt->hash[i]), 32); @@ -1034,10 +1202,35 @@ static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { if (op == (BeamInstr) BeamOp(op_i_time_breakpoint)) { BpDataTime *bdt = (BpDataTime *) bd; Uint i = 0; + Uint j = 0; + Process *h_p = NULL; + bp_data_time_item_t *item = NULL; + process_breakpoint_time_t *pbt = NULL; + + /* remove all psd associated with the hash + * and then delete the hash. + * ... sigh ... + */ + for( i = 0; i < bdt->n; ++i) { + if (bdt->hash[i].used) { + for (j = 0; j < bdt->hash[i].n; ++j) { + item = &(bdt->hash[i].item[j]); + if (item->pid != NIL) { + h_p = process_tab[internal_pid_index(item->pid)]; + if (h_p) { + pbt = ERTS_PROC_SET_CALL_TIME(h_p, ERTS_PROC_LOCK_MAIN, NULL); + if (pbt) { + Free(pbt); + } + } + } + } + } bp_hash_delete(&(bdt->hash[i])); } Free(bdt->hash); + bdt->hash = NULL; bdt->n = 0; } Free(bd); diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index 58fd65399d..515a06e220 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -24,7 +24,6 @@ #include "sys.h" #include "erl_vm.h" #include "global.h" -//#include "hash.h" @@ -93,6 +92,7 @@ typedef struct bp_data_time { /* Call time */ struct bp_data *next; struct bp_data *prev; Uint orig_instr; + Uint pause; Uint n; bp_time_hash_t *hash; } BpDataTime; @@ -106,6 +106,13 @@ typedef struct { extern erts_smp_spinlock_t erts_bp_lock; +#define ERTS_BP_CALL_TIME_SCHEDULE_IN (0) +#define ERTS_BP_CALL_TIME_SCHEDULE_OUT (1) +#define ERTS_BP_CALL_TIME_SCHEDULE_EXITING (2) + +#define ERTS_BP_CALL_TIME_CALL (0) +#define ERTS_BP_CALL_TIME_RETURN (1) +#define ERTS_BP_CALL_TIME_TAIL_CALL (2) #ifdef ERTS_SMP #define ErtsSmpBPLock(BDC) erts_smp_spin_lock(&erts_bp_lock) @@ -214,7 +221,8 @@ int erts_is_native_break(BeamInstr *pc); int erts_is_count_break(BeamInstr *pc, Sint *count_ret); int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *call_time); -void erts_do_time_break(Process *p, BpDataTime *bdt); +void erts_do_time_break(Process *p, BpDataTime *bdt, Uint type); +void erts_schedule_time_break(Process *p, Uint out); BeamInstr *erts_find_local_func(Eterm mfa[3]); diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 145ae48590..8661d7cbf5 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -228,9 +228,10 @@ BeamInstr* em_call_traced_function; ** for the refering variable (one of these), and rouge references ** will most likely cause chaos. */ -BeamInstr beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */ -BeamInstr beam_return_trace[1]; /* OpCode(i_return_trace) */ -BeamInstr beam_exception_trace[1]; /* UGLY also OpCode(i_return_trace) */ +BeamInstr beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */ +BeamInstr beam_return_trace[1]; /* OpCode(i_return_trace) */ +BeamInstr beam_exception_trace[1]; /* UGLY also OpCode(i_return_trace) */ +BeamInstr beam_time_return_trace[1]; /* OpCode(i_time_return_trace) */ /* * All Beam instructions in numerical order. @@ -4411,6 +4412,7 @@ apply_bif_or_nif_epilogue: OpCase(i_time_breakpoint): { BeamInstr real_I; BpDataTime *bdt = (BpDataTime *) (I)[-4]; + Uint tail_call = 0; ASSERT((I)[-5] == (Uint) BeamOp(op_i_func_info_IaaI)); ASSERT(bdt); @@ -4418,12 +4420,53 @@ apply_bif_or_nif_epilogue: ASSERT(bdt); (I)[-4] = (Uint) bdt; real_I = bdt->orig_instr; - erts_do_time_break(c_p, bdt); - ASSERT(VALID_INSTR(real_I)); + + if (IS_TRACED_FL(c_p, F_TRACE_CALLS) && !(bdt->pause)) { + if (*cp_val((Eterm)c_p->cp) == (BeamInstr) OpCode(i_time_return_trace)) { + tail_call = 1; + } + + if (tail_call) { + /* This _IS_ a tail recursive call */ + SWAPOUT; + erts_do_time_break(c_p, bdt, ERTS_BP_CALL_TIME_TAIL_CALL); + SWAPIN; + } else { + SWAPOUT; + erts_do_time_break(c_p, bdt, ERTS_BP_CALL_TIME_CALL); + + ASSERT(c_p->htop <= E && E <= c_p->hend); + if (E - 2 < HTOP) { + PROCESS_MAIN_CHK_LOCKS(c_p); + FCALLS -= erts_garbage_collect(c_p, 2, reg, I[-1]); + PROCESS_MAIN_CHK_LOCKS(c_p); + } + SWAPIN; + + ASSERT(c_p->htop <= E && E <= c_p->hend); + + E -= 2; + E[1] = make_cp(c_p->cp); /* original return address */ + E[0] = make_cp((Uint *)bdt); /* make sure bdt is allocated in low mem (for halfword) */ + c_p->cp = (Eterm *) make_cp(beam_time_return_trace); + } + } + Goto(real_I); } + OpCase(i_time_return_trace): { + BpDataTime *bdt = (BpDataTime *) cp_val((E)[0]); + SWAPOUT; + erts_do_time_break(c_p, bdt, ERTS_BP_CALL_TIME_RETURN); + SWAPIN; + c_p->cp = NULL; + SET_I((Eterm *) E[1]); + E += 2; + Goto(*I); + } + OpCase(i_trace_breakpoint): if (! IS_TRACED_FL(c_p, F_TRACE_CALLS)) { BeamInstr real_I; @@ -4973,13 +5016,15 @@ apply_bif_or_nif_epilogue: em_call_error_handler = OpCode(call_error_handler); em_call_traced_function = OpCode(call_traced_function); em_apply_bif = OpCode(apply_bif); - beam_apply[0] = (BeamInstr) OpCode(i_apply); - beam_apply[1] = (BeamInstr) OpCode(normal_exit); - beam_exit[0] = (BeamInstr) OpCode(error_action_code); - beam_continue_exit[0] = (BeamInstr) OpCode(continue_exit); - beam_return_to_trace[0] = (BeamInstr) OpCode(i_return_to_trace); - beam_return_trace[0] = (BeamInstr) OpCode(return_trace); - beam_exception_trace[0] = (BeamInstr) OpCode(return_trace); /* UGLY */ + + beam_apply[0] = (BeamInstr) OpCode(i_apply); + beam_apply[1] = (BeamInstr) OpCode(normal_exit); + beam_exit[0] = (BeamInstr) OpCode(error_action_code); + beam_continue_exit[0] = (BeamInstr) OpCode(continue_exit); + beam_return_to_trace[0] = (BeamInstr) OpCode(i_return_to_trace); + beam_return_trace[0] = (BeamInstr) OpCode(return_trace); + beam_exception_trace[0] = (BeamInstr) OpCode(return_trace); /* UGLY */ + beam_time_return_trace[0] = (BeamInstr) OpCode(i_time_return_trace); /* * Enter all BIFs into the export table. diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 26390d397c..1e626533a4 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -41,11 +41,11 @@ #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) static erts_smp_mtx_t trace_pattern_mutex; -const struct trace_pattern_flags erts_trace_pattern_flags_off = {0, 0, 0, 0}; +const struct trace_pattern_flags erts_trace_pattern_flags_off = {0, 0, 0, 0, 0}; static int erts_default_trace_pattern_is_on; static Binary *erts_default_match_spec; static Binary *erts_default_meta_match_spec; -static struct trace_pattern_flags erts_default_trace_pattern_flags; +struct trace_pattern_flags erts_default_trace_pattern_flags; static Eterm erts_default_meta_tracer_pid; static void new_seq_trace_token(Process* p); /* help func for seq_trace_2*/ @@ -1058,7 +1058,22 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) mfa[1] = tp[2]; mfa[2] = signed_val(tp[3]); +#ifdef ERTS_SMP + if ( (key == am_call_time) || (key == am_all)) { + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN); + erts_smp_block_system(0); + } +#endif + r = function_is_traced(p, mfa, &ms, &ms_meta, &meta, &count, &call_time); + +#ifdef ERTS_SMP + if ( (key == am_call_time) || (key == am_all)) { + erts_smp_release_system(); + erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN); + } +#endif + switch (r) { case FUNC_TRACE_NOEXIST: UnUseTmpHeap(3,p); @@ -1429,6 +1444,7 @@ erts_set_trace_pattern(Eterm* mfa, int specified, erts_clear_trace_break(mfa, specified); erts_clear_mtrace_break(mfa, specified); erts_clear_count_break(mfa, specified); + erts_clear_time_break(mfa, specified); } else { int m = 0; if (flags.local) { @@ -1460,7 +1476,7 @@ erts_set_trace_pattern(Eterm* mfa, int specified, if (flags.call_count) { m = erts_clear_count_break(mfa, specified); } - if (flags.call_count) { + if (flags.call_time) { m = erts_clear_time_break(mfa, specified); } /* All assignments to 'm' above should give the same value, diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 4befa0aca9..5293fabe87 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -38,6 +38,7 @@ #include "erl_instrument.h" #include "erl_threads.h" #include "erl_binary.h" +#include "beam_bp.h" #define ERTS_RUNQ_CHECK_BALANCE_REDS_PER_SCHED (2000*CONTEXT_REDS) #define ERTS_RUNQ_CALL_CHECK_BALANCE_REDS \ @@ -5877,6 +5878,11 @@ Process *schedule(Process *p, int calls) } if (IS_TRACED(p)) { + if (IS_TRACED_FL(p, F_TRACE_CALLS) && + erts_default_trace_pattern_flags.call_time && + p->status != P_FREE) { + erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_OUT); + } switch (p->status) { case P_EXITING: if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT)) @@ -6313,7 +6319,11 @@ Process *schedule(Process *p, int calls) trace_virtual_sched(p, am_in); break; } + if (IS_TRACED_FL(p, F_TRACE_CALLS) && erts_default_trace_pattern_flags.call_time) { + erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_IN); + } } + if (p->status != P_EXITING) p->status = P_RUNNING; @@ -8056,8 +8066,13 @@ erts_do_exit_process(Process* p, Eterm reason) ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p); #endif - if (IS_TRACED_FL(p,F_TRACE_PROCS)) - trace_proc(p, p, am_exit, reason); + if (IS_TRACED(p)) { + if (IS_TRACED_FL(p, F_TRACE_CALLS) && erts_default_trace_pattern_flags.call_time) + erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_EXITING); + + if (IS_TRACED_FL(p,F_TRACE_PROCS)) + trace_proc(p, p, am_exit, reason); + } erts_trace_check_exiting(p->id); @@ -8106,6 +8121,8 @@ continue_exit_process(Process *p Eterm reason = p->fvalue; DistEntry *dep; struct saved_calls *scb; + process_breakpoint_time_t *pbt; + #ifdef DEBUG int yield_allowed = 1; #endif @@ -8245,6 +8262,7 @@ continue_exit_process(Process *p ? ERTS_PROC_SET_DIST_ENTRY(p, ERTS_PROC_LOCKS_ALL, NULL) : NULL); scb = ERTS_PROC_SET_SAVED_CALLS_BUF(p, ERTS_PROC_LOCKS_ALL, NULL); + pbt = ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCKS_ALL, NULL); erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL); processes_busy--; @@ -8287,6 +8305,9 @@ continue_exit_process(Process *p if (scb) erts_free(ERTS_ALC_T_CALLS_BUF, (void *) scb); + if (pbt) + erts_free(ERTS_ALC_T_BPD, (void *) pbt); + delete_process(p); erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 711b613028..8d087fd7d6 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1744,6 +1744,7 @@ struct trace_pattern_flags { unsigned int call_time : 1; /* Fast call time breakpoint */ }; extern const struct trace_pattern_flags erts_trace_pattern_flags_off; +extern struct trace_pattern_flags erts_default_trace_pattern_flags; /* used with blocksystem */ int erts_set_trace_pattern(Eterm* mfa, int specified, Binary* match_prog_set, Binary *meta_match_prog_set, int on, struct trace_pattern_flags, -- cgit v1.2.3 From 5bd5aa849475b505a5b0798295511a6a0b4885a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 30 Apr 2010 15:03:56 +0200 Subject: Teach call_time trace to use intruction pointers call_time trace will use instruction pointers instead of breakpoint data pointers. More costly lookup but the bdt structure might be deallocated, we do not want that. Remove unnecessary pattern lock. --- erts/emulator/beam/beam_bp.c | 190 ++++++++++++++++++++---------------- erts/emulator/beam/beam_bp.h | 29 +----- erts/emulator/beam/beam_emu.c | 49 +++++++--- erts/emulator/beam/erl_bif_trace.c | 14 +-- erts/emulator/beam/erl_lock_check.c | 1 - erts/emulator/beam/erl_process.c | 8 +- erts/emulator/beam/global.h | 2 +- 7 files changed, 153 insertions(+), 140 deletions(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 48e7b6675a..3e776fa951 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -549,6 +549,8 @@ static void bp_hash_rehash(bp_time_hash_t *hash, Uint n) { item[ix].pid = NIL; } + /* rehash, old hash -> new hash */ + for( ix = 0; ix < hash->n; ix++) { if (hash->item[ix].pid != NIL) { @@ -654,45 +656,51 @@ void erts_schedule_time_break(Process *p, Uint schedule) { Uint ix = 0; bp_data_time_item_t sitem, *item = NULL; bp_time_hash_t *h = NULL; + BpDataTime *pbdt = NULL; ASSERT(p); pbt = ERTS_PROC_GET_CALL_TIME(p); +#ifdef ERTS_SMP + ix = p->scheduler_data->no - 1; +#else + ix = 0; +#endif +/* ASSERT( (p->status == P_RUNNING) || (p->status == P_WAITING) || (p->status == P_RUNABLE)); - +*/ if (pbt) { get_sys_now(&ms,&s,&us); switch(schedule) { case ERTS_BP_CALL_TIME_SCHEDULE_EXITING : + break; case ERTS_BP_CALL_TIME_SCHEDULE_OUT : /* When a process is scheduled _out_, * timestamp it and add its delta to * the previous breakpoint. */ -#ifdef ERTS_SMP - ix = p->scheduler_data->no - 1; -#else - ix = 0; -#endif - bp_time_diff(&sitem, pbt, ms, s, us); - sitem.pid = p->id; - sitem.count = 1; - /*count is set to 0 if out and 1 if exiting */ + pbdt = (BpDataTime *) is_break(pbt->pc, (Uint) BeamOp(op_i_time_breakpoint)); + if (pbdt) { + bp_time_diff(&sitem, pbt, ms, s, us); + sitem.pid = p->id; + sitem.count = 0; - h = &(pbt->bdt->hash[ix]); + h = &(pbdt->hash[ix]); - ASSERT(h->item); + ASSERT(h); + ASSERT(h->item); - item = bp_hash_get(h, &sitem); - if (!item) { - item = bp_hash_put(h, &sitem); - } else { - BP_TIME_ADD(item, &sitem); + item = bp_hash_get(h, &sitem); + if (!item) { + item = bp_hash_put(h, &sitem); + } else { + BP_TIME_ADD(item, &sitem); + } } break; case ERTS_BP_CALL_TIME_SCHEDULE_IN : @@ -700,21 +708,6 @@ void erts_schedule_time_break(Process *p, Uint schedule) { * timestamp it and remove the previous * timestamp in the psd. */ - sitem.pid = p->id; - sitem.count = -1; - sitem.us_time = 0; - sitem.s_time = 0; - - h = &(pbt->bdt->hash[ix]); - - ASSERT(h->item); - - item = bp_hash_get(h, &sitem); - if (!item) { - item = bp_hash_put(h, &sitem); - } else { - BP_TIME_ADD(item, &sitem); - } pbt->ms = ms; pbt->s = s; pbt->us = us; @@ -748,15 +741,15 @@ void erts_schedule_time_break(Process *p, Uint schedule) { * - egil */ -void erts_do_time_break(Process *p, BpDataTime *bdt, Uint type) { +void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type) { Uint ms,s,us; process_breakpoint_time_t *pbt = NULL; int ix = 0; bp_data_time_item_t sitem, *item = NULL; bp_time_hash_t *h = NULL; + BpDataTime *pbdt = NULL; ASSERT(p); - ASSERT(bdt); ASSERT(p->status == P_RUNNING); /* get previous timestamp and breakpoint @@ -765,61 +758,81 @@ void erts_do_time_break(Process *p, BpDataTime *bdt, Uint type) { pbt = ERTS_PROC_GET_CALL_TIME(p); get_sys_now(&ms,&s,&us); +#ifdef ERTS_SMP + ix = p->scheduler_data->no - 1; +#else + ix = 0; +#endif + switch(type) { - case ERTS_BP_CALL_TIME_CALL: /* get pbt * timestamp = t0 + * lookup bdt from code * set ts0 to pbt * add call count here? */ - if (!pbt) { - pbt = Alloc(sizeof(process_breakpoint_time_t)); - (void *) ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCK_MAIN, pbt); - } - - pbt->bdt = bdt; /* needed for schedule? */ - - pbt->ms = ms; - pbt->s = s; - pbt->us = us; - - - break; + case ERTS_BP_CALL_TIME_CALL: case ERTS_BP_CALL_TIME_TAIL_CALL: - ASSERT(pbt); - -#ifdef ERTS_SMP - ix = p->scheduler_data->no - 1; -#else - ix = 0; -#endif + if (pbt) { + ASSERT(pbt->pc); + /* add time to previous code */ bp_time_diff(&sitem, pbt, ms, s, us); sitem.pid = p->id; - sitem.count = 1; + sitem.count = 0; - h = &(bdt->hash[ix]); + /* previous breakpoint */ + pbdt = (BpDataTime *) is_break(pbt->pc, (Uint) BeamOp(op_i_time_breakpoint)); - ASSERT(h->item); + /* if null then the breakpoint was removed */ + if (pbdt) { + h = &(pbdt->hash[ix]); - item = bp_hash_get(h, &sitem); - if (!item) { - item = bp_hash_put(h, &sitem); - } else { - BP_TIME_ADD(item, &sitem); + ASSERT(h); + ASSERT(h->item); + + item = bp_hash_get(h, &sitem); + if (!item) { + item = bp_hash_put(h, &sitem); + } else { + BP_TIME_ADD(item, &sitem); + } } + } else { + /* first call of process to instrumented function */ + pbt = Alloc(sizeof(process_breakpoint_time_t)); + (void *) ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCK_MAIN, pbt); + } + /* add count to this code */ + sitem.pid = p->id; + sitem.count = 1; + sitem.s_time = 0; + sitem.us_time = 0; - pbt->bdt = bdt; /* needed for schedule? */ + /* this breakpoint */ + ASSERT(bdt); + h = &(bdt->hash[ix]); - pbt->ms = ms; - pbt->s = s; - pbt->us = us; - break; + ASSERT(h); + ASSERT(h->item); + item = bp_hash_get(h, &sitem); + if (!item) { + item = bp_hash_put(h, &sitem); + } else { + BP_TIME_ADD(item, &sitem); + } + + pbt->pc = pc; + pbt->ms = ms; + pbt->s = s; + pbt->us = us; + break; case ERTS_BP_CALL_TIME_RETURN: /* get pbt + * lookup bdt from code * timestamp = t1 * get ts0 from pbt * get item from bdt->hash[bp_hash(p->id)] @@ -830,30 +843,34 @@ void erts_do_time_break(Process *p, BpDataTime *bdt, Uint type) { /* might have been removed due to * trace_pattern(false) */ -#ifdef ERTS_SMP - ix = p->scheduler_data->no - 1; -#else - ix = 0; -#endif + ASSERT(pbt->pc); + bp_time_diff(&sitem, pbt, ms, s, us); sitem.pid = p->id; - sitem.count = 1; + sitem.count = 0; - h = &(bdt->hash[ix]); + /* previous breakpoint */ + pbdt = (BpDataTime *) is_break(pbt->pc, (Uint) BeamOp(op_i_time_breakpoint)); - ASSERT(h->item); + if (pbdt) { - item = bp_hash_get(h, &sitem); - if (!item) { - item = bp_hash_put(h, &sitem); - } else { - BP_TIME_ADD(item, &sitem); + h = &(pbdt->hash[ix]); + + ASSERT(h); + ASSERT(h->item); + + item = bp_hash_get(h, &sitem); + if (!item) { + item = bp_hash_put(h, &sitem); + } else { + BP_TIME_ADD(item, &sitem); + } } - pbt->bdt = NULL; - pbt->ms = ms; - pbt->s = s; - pbt->us = us; + pbt->pc = pc; + pbt->ms = ms; + pbt->s = s; + pbt->us = us; } break; } @@ -976,7 +993,8 @@ static int set_function_break(Module *modp, BeamInstr *pc, BpDataTime *bdt = (BpDataTime *) bd; Uint i = 0; - /*XXX: must block system */ + ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0)); + if (count_op == erts_break_stop) { bdt->pause = 1; } else { diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index 515a06e220..249cb707fd 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -101,7 +101,7 @@ typedef struct { Uint ms; Uint s; Uint us; - BpDataTime *bdt; + Uint *pc; } process_breakpoint_time_t; /* used within psd */ extern erts_smp_spinlock_t erts_bp_lock; @@ -137,27 +137,6 @@ do { \ *(instr_result) = bdc->orig_instr; \ } while (0) -#define ErtsTimeBreak(pc,instr_result) \ -do { \ - BpDataTime *bdt = (BpDataTime *) (pc)[-4]; \ - Uint ms,s,u; \ - \ - ASSERT((pc)[-5] == (Uint) BeamOp(op_i_func_info_IaaI)); \ - ASSERT(bdt); \ - bdt = (BpDataTime *) bdt->next; \ - ASSERT(bdc); \ - (pc)[-4] = (Uint) bdt; \ - get_now(&ms,&s,&u); \ - ErtsSmpBPLock(bdt); \ - if (bdt->count >= 0) \ - bdt->count++; \ - bdt->s_time = 1; \ - bdt->us_time = 1; \ - ErtsSmpBPUnlock(bdt); \ - *(instr_result) = bdt->orig_instr; \ -} while (0) - - #define ErtsBreakSkip(pc,instr_result) \ do { \ BpData *bd = (BpData *) (pc)[-4]; \ @@ -198,8 +177,6 @@ int erts_set_debug_break(Eterm mfa[3], int specified); int erts_clear_debug_break(Eterm mfa[3], int specified); int erts_set_count_break(Eterm mfa[3], int specified, enum erts_break_op); int erts_clear_count_break(Eterm mfa[3], int specified); -int erts_set_time_break(Eterm mfa[3], int specified, enum erts_break_op); -int erts_clear_time_break(Eterm mfa[3], int specified); int erts_clear_break(Eterm mfa[3], int specified); @@ -221,8 +198,10 @@ int erts_is_native_break(BeamInstr *pc); int erts_is_count_break(BeamInstr *pc, Sint *count_ret); int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *call_time); -void erts_do_time_break(Process *p, BpDataTime *bdt, Uint type); +void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type); void erts_schedule_time_break(Process *p, Uint out); +int erts_set_time_break(Eterm mfa[3], int specified, enum erts_break_op); +int erts_clear_time_break(Eterm mfa[3], int specified); BeamInstr *erts_find_local_func(Eterm mfa[3]); diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 8661d7cbf5..8eb198fac3 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -231,7 +231,7 @@ BeamInstr* em_call_traced_function; BeamInstr beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */ BeamInstr beam_return_trace[1]; /* OpCode(i_return_trace) */ BeamInstr beam_exception_trace[1]; /* UGLY also OpCode(i_return_trace) */ -BeamInstr beam_time_return_trace[1]; /* OpCode(i_time_return_trace) */ +BeamInstr beam_return_time_trace[1]; /* OpCode(i_time_return_trace) */ /* * All Beam instructions in numerical order. @@ -4409,6 +4409,10 @@ apply_bif_or_nif_epilogue: Goto(real_I); } + /* need to send mfa instead of bdt pointer + * the pointer might be deallocated. + */ + OpCase(i_time_breakpoint): { BeamInstr real_I; BpDataTime *bdt = (BpDataTime *) (I)[-4]; @@ -4423,43 +4427,48 @@ apply_bif_or_nif_epilogue: ASSERT(VALID_INSTR(real_I)); if (IS_TRACED_FL(c_p, F_TRACE_CALLS) && !(bdt->pause)) { - if (*cp_val((Eterm)c_p->cp) == (BeamInstr) OpCode(i_time_return_trace)) { + if (*cp_val((Eterm)c_p->cp) == (BeamInstr) OpCode(i_return_time_trace)) { tail_call = 1; } if (tail_call) { /* This _IS_ a tail recursive call */ SWAPOUT; - erts_do_time_break(c_p, bdt, ERTS_BP_CALL_TIME_TAIL_CALL); + erts_trace_time_break(c_p, I, bdt, ERTS_BP_CALL_TIME_TAIL_CALL); SWAPIN; } else { SWAPOUT; - erts_do_time_break(c_p, bdt, ERTS_BP_CALL_TIME_CALL); + erts_trace_time_break(c_p, I, bdt, ERTS_BP_CALL_TIME_CALL); + /* r register needs to be copied to the array + * for the garbage collector + */ ASSERT(c_p->htop <= E && E <= c_p->hend); if (E - 2 < HTOP) { + reg[0] = r(0); PROCESS_MAIN_CHK_LOCKS(c_p); FCALLS -= erts_garbage_collect(c_p, 2, reg, I[-1]); PROCESS_MAIN_CHK_LOCKS(c_p); + r(0) = reg[0]; } SWAPIN; ASSERT(c_p->htop <= E && E <= c_p->hend); E -= 2; + E[0] = make_cp(I); E[1] = make_cp(c_p->cp); /* original return address */ - E[0] = make_cp((Uint *)bdt); /* make sure bdt is allocated in low mem (for halfword) */ - c_p->cp = (Eterm *) make_cp(beam_time_return_trace); + c_p->cp = (Eterm *) make_cp(beam_return_time_trace); } } Goto(real_I); } - OpCase(i_time_return_trace): { - BpDataTime *bdt = (BpDataTime *) cp_val((E)[0]); + OpCase(i_return_time_trace): { + Uint *pc = (Uint *) E[0]; SWAPOUT; - erts_do_time_break(c_p, bdt, ERTS_BP_CALL_TIME_RETURN); + erts_trace_time_break(c_p, pc, NULL, ERTS_BP_CALL_TIME_RETURN); SWAPIN; c_p->cp = NULL; SET_I((Eterm *) E[1]); @@ -5024,7 +5033,7 @@ apply_bif_or_nif_epilogue: beam_return_to_trace[0] = (BeamInstr) OpCode(i_return_to_trace); beam_return_trace[0] = (BeamInstr) OpCode(return_trace); beam_exception_trace[0] = (BeamInstr) OpCode(return_trace); /* UGLY */ - beam_time_return_trace[0] = (BeamInstr) OpCode(i_time_return_trace); + beam_return_time_trace[0] = (BeamInstr) OpCode(i_return_time_trace); /* * Enter all BIFs into the export table. @@ -5228,14 +5237,18 @@ next_catch(Process* c_p, Eterm *reg) { int active_catches = c_p->catches > 0; int have_return_to_trace = 0; Eterm *ptr, *prev, *return_to_trace_ptr = NULL; - BeamInstr i_return_trace = beam_return_trace[0]; - BeamInstr i_return_to_trace = beam_return_to_trace[0]; + + BeamInstr i_return_trace = beam_return_trace[0]; + BeamInstr i_return_to_trace = beam_return_to_trace[0]; + BeamInstr i_return_time_trace = beam_return_time_trace[0]; + ptr = prev = c_p->stop; ASSERT(is_CP(*ptr)); ASSERT(ptr <= STACK_START(c_p)); if (ptr == STACK_START(c_p)) return NULL; if ((is_not_CP(*ptr) || (*cp_val(*ptr) != i_return_trace && - *cp_val(*ptr) != i_return_to_trace)) + *cp_val(*ptr) != i_return_to_trace && + *cp_val(*ptr) != i_return_time_trace )) && c_p->cp) { /* Can not follow cp here - code may be unloaded */ BeamInstr *cpp = c_p->cp; @@ -5247,6 +5260,9 @@ next_catch(Process* c_p, Eterm *reg) { } else if (cpp == beam_return_trace) { /* Skip return_trace parameters */ ptr += 2; + } else if (cpp == beam_return_time_trace) { + /* Skip return_trace parameters */ + ptr += 1; } else if (cpp == beam_return_to_trace) { have_return_to_trace = !0; /* Record next cp */ } @@ -5276,6 +5292,13 @@ next_catch(Process* c_p, Eterm *reg) { } have_return_to_trace = !0; /* Record next cp */ return_to_trace_ptr = NULL; + } else if (*cp_val(*prev) == i_return_time_trace) { + /* Skip stack frame variables */ + while (++ptr, ptr < STACK_START(c_p) && is_not_CP(*ptr)) { + if (is_catch(*ptr) && active_catches) goto found_catch; + } + /* Skip return_trace parameters */ + ptr += 1; } else { if (have_return_to_trace) { /* Record this cp as possible return_to trace cp */ diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 1e626533a4..856669d2c9 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -40,12 +40,11 @@ #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) -static erts_smp_mtx_t trace_pattern_mutex; const struct trace_pattern_flags erts_trace_pattern_flags_off = {0, 0, 0, 0, 0}; static int erts_default_trace_pattern_is_on; static Binary *erts_default_match_spec; static Binary *erts_default_meta_match_spec; -struct trace_pattern_flags erts_default_trace_pattern_flags; +static struct trace_pattern_flags erts_default_trace_pattern_flags; static Eterm erts_default_meta_tracer_pid; static void new_seq_trace_token(Process* p); /* help func for seq_trace_2*/ @@ -65,7 +64,6 @@ static void clear_trace_bif(int bif_index); void erts_bif_trace_init(void) { - erts_smp_mtx_init(&trace_pattern_mutex, "trace_pattern"); erts_default_trace_pattern_is_on = 0; erts_default_match_spec = NULL; erts_default_meta_match_spec = NULL; @@ -203,8 +201,8 @@ trace_pattern_3(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) goto error; } - if (match_prog_set && !flags.local && !flags.meta && flags.call_count) { - /* A match prog is not allowed with just call_count */ + if (match_prog_set && !flags.local && !flags.meta && (flags.call_count || flags.call_time)) { + /* A match prog is not allowed with just call_count or call_time*/ goto error; } @@ -350,7 +348,6 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on, struct trace_pattern_flags *trace_pattern_flags, Eterm *meta_tracer_pid) { - erts_smp_mtx_lock(&trace_pattern_mutex); if (trace_pattern_is_on) *trace_pattern_is_on = erts_default_trace_pattern_is_on; if (match_spec) @@ -361,7 +358,6 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on, *trace_pattern_flags = erts_default_trace_pattern_flags; if (meta_tracer_pid) *meta_tracer_pid = erts_default_meta_tracer_pid; - erts_smp_mtx_unlock(&trace_pattern_mutex); } @@ -964,8 +960,8 @@ static int function_is_traced(Process *p, Binary **ms, /* out */ Binary **ms_meta, /* out */ Eterm *tracer_pid_meta, /* out */ - Sint *count, /* out */ - Eterm *call_time) /* out */ + Sint *count, /* out */ + Eterm *call_time) /* out */ { Export e; Export* ep; diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 6ff5c1b9da..cee470ae37 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -105,7 +105,6 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "node_table", NULL }, { "dist_table", NULL }, { "sys_tracers", NULL }, - { "trace_pattern", NULL }, { "module_tab", NULL }, { "export_tab", NULL }, { "fun_tab", NULL }, diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 5293fabe87..f4c889889d 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -5878,9 +5878,7 @@ Process *schedule(Process *p, int calls) } if (IS_TRACED(p)) { - if (IS_TRACED_FL(p, F_TRACE_CALLS) && - erts_default_trace_pattern_flags.call_time && - p->status != P_FREE) { + if (IS_TRACED_FL(p, F_TRACE_CALLS) && p->status != P_FREE) { erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_OUT); } switch (p->status) { @@ -6319,7 +6317,7 @@ Process *schedule(Process *p, int calls) trace_virtual_sched(p, am_in); break; } - if (IS_TRACED_FL(p, F_TRACE_CALLS) && erts_default_trace_pattern_flags.call_time) { + if (IS_TRACED_FL(p, F_TRACE_CALLS)) { erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_IN); } } @@ -8067,7 +8065,7 @@ erts_do_exit_process(Process* p, Eterm reason) #endif if (IS_TRACED(p)) { - if (IS_TRACED_FL(p, F_TRACE_CALLS) && erts_default_trace_pattern_flags.call_time) + if (IS_TRACED_FL(p, F_TRACE_CALLS)) erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_EXITING); if (IS_TRACED_FL(p,F_TRACE_PROCS)) diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 8d087fd7d6..b042f14a7b 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1744,7 +1744,7 @@ struct trace_pattern_flags { unsigned int call_time : 1; /* Fast call time breakpoint */ }; extern const struct trace_pattern_flags erts_trace_pattern_flags_off; -extern struct trace_pattern_flags erts_default_trace_pattern_flags; /* used with blocksystem */ +extern int erts_call_time_breakpoint_tracing; int erts_set_trace_pattern(Eterm* mfa, int specified, Binary* match_prog_set, Binary *meta_match_prog_set, int on, struct trace_pattern_flags, -- cgit v1.2.3 From 19278f9eecb61ab652347097d1558b696c8eb066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 6 May 2010 14:38:20 +0200 Subject: Fix BeamInstr for call_time --- erts/emulator/beam/beam_bp.h | 2 +- erts/emulator/beam/beam_emu.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index 249cb707fd..4a3d244d2f 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -91,7 +91,7 @@ typedef struct { typedef struct bp_data_time { /* Call time */ struct bp_data *next; struct bp_data *prev; - Uint orig_instr; + BeamInstr orig_instr; Uint pause; Uint n; bp_time_hash_t *hash; diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 8eb198fac3..29d0c0afde 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4418,11 +4418,11 @@ apply_bif_or_nif_epilogue: BpDataTime *bdt = (BpDataTime *) (I)[-4]; Uint tail_call = 0; - ASSERT((I)[-5] == (Uint) BeamOp(op_i_func_info_IaaI)); + ASSERT((I)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); ASSERT(bdt); bdt = (BpDataTime *) bdt->next; ASSERT(bdt); - (I)[-4] = (Uint) bdt; + (I)[-4] = (BeamInstr) bdt; real_I = bdt->orig_instr; ASSERT(VALID_INSTR(real_I)); @@ -4466,12 +4466,12 @@ apply_bif_or_nif_epilogue: } OpCase(i_return_time_trace): { - Uint *pc = (Uint *) E[0]; + BeamInstr *pc = (BeamInstr *) (UWord) E[0]; SWAPOUT; erts_trace_time_break(c_p, pc, NULL, ERTS_BP_CALL_TIME_RETURN); SWAPIN; c_p->cp = NULL; - SET_I((Eterm *) E[1]); + SET_I((BeamInstr *) cp_val(E[1])); E += 2; Goto(*I); } @@ -4485,7 +4485,7 @@ apply_bif_or_nif_epilogue: } /* Fall through to next case */ OpCase(i_mtrace_breakpoint): { - Uint real_I; + BeamInstr real_I; Uint32 flags; Eterm tracer_pid; Uint *cpp; -- cgit v1.2.3 From 9a5848fc667acb2aedeb39af94d16a9b346aa47e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 6 May 2010 18:51:34 +0200 Subject: Add search keys in breakpoints and a searchfunction --- erts/emulator/beam/beam_bp.c | 29 ++++++++++++++++++++++++++--- erts/emulator/beam/beam_bp.h | 15 ++++++++++----- 2 files changed, 36 insertions(+), 8 deletions(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 3e776fa951..7b46f073c6 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -127,6 +127,7 @@ static int clear_function_break(Module *modp, BeamInstr *pc, BeamInstr break_op); static BpData *is_break(BeamInstr *pc, BeamInstr break_op); +static BpData *get_break(BeamInstr *pc, BeamInstr break_op); /* bp_hash */ #define BP_TIME_ADD(pi0, pi1) \ @@ -684,7 +685,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) { * the previous breakpoint. */ - pbdt = (BpDataTime *) is_break(pbt->pc, (Uint) BeamOp(op_i_time_breakpoint)); + pbdt = (BpDataTime *) get_break(pbt->pc, (Uint) BeamOp(op_i_time_breakpoint)); if (pbdt) { bp_time_diff(&sitem, pbt, ms, s, us); sitem.pid = p->id; @@ -782,7 +783,7 @@ void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type sitem.count = 0; /* previous breakpoint */ - pbdt = (BpDataTime *) is_break(pbt->pc, (Uint) BeamOp(op_i_time_breakpoint)); + pbdt = (BpDataTime *) get_break(pbt->pc, (Uint) BeamOp(op_i_time_breakpoint)); /* if null then the breakpoint was removed */ if (pbdt) { @@ -850,7 +851,7 @@ void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type sitem.count = 0; /* previous breakpoint */ - pbdt = (BpDataTime *) is_break(pbt->pc, (Uint) BeamOp(op_i_time_breakpoint)); + pbdt = (BpDataTime *) get_break(pbt->pc, (Uint) BeamOp(op_i_time_breakpoint)); if (pbdt) { @@ -1076,6 +1077,7 @@ static int set_function_break(Module *modp, BeamInstr *pc, *r = bd; } } + bd->this_instr = break_op; /* Init the bp type specific data */ if (break_op == (BeamInstr) BeamOp(op_i_trace_breakpoint) || break_op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) { @@ -1296,3 +1298,24 @@ static BpData *is_break(BeamInstr *pc, BeamInstr break_op) { } return NULL; } +static BpData *get_break(BeamInstr *pc, BeamInstr break_op) { + ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); + if (! erts_is_native_break(pc)) { + BpData *bd = (BpData *) pc[-4]; + + if (! bd){ + return NULL; + } + + bd = bd->next; + while (bd != (BpData *) pc[-4]) { + ASSERT(bd); + if (bd->this_instr == break_op) { + ASSERT(bd); + return bd; + } + bd = bd->next; + } + } + return NULL; +} diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index 4a3d244d2f..6b339401fc 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -49,6 +49,7 @@ typedef struct bp_data { struct bp_data *next; /* Doubly linked ring pointers */ struct bp_data *prev; /* -"- */ BeamInstr orig_instr; /* The original instruction to execute */ + BeamInstr this_instr; /* key */ } BpData; /* ** All the following bp_data_.. structs must begin the same way @@ -57,21 +58,24 @@ typedef struct bp_data { typedef struct bp_data_trace { struct bp_data *next; struct bp_data *prev; - BeamInstr orig_instr; + BeamInstr orig_instr; + BeamInstr this_instr; /* key */ Binary *match_spec; - Eterm tracer_pid; + Eterm tracer_pid; } BpDataTrace; typedef struct bp_data_debug { struct bp_data *next; struct bp_data *prev; - BeamInstr orig_instr; + BeamInstr orig_instr; + BeamInstr this_instr; /* key */ } BpDataDebug; -typedef struct bp_data_count { /* Call count */ +typedef struct bp_data_count { /* Call count */ struct bp_data *next; struct bp_data *prev; BeamInstr orig_instr; + BeamInstr this_instr; /* key */ Sint count; } BpDataCount; @@ -88,10 +92,11 @@ typedef struct { bp_data_time_item_t *item; } bp_time_hash_t; -typedef struct bp_data_time { /* Call time */ +typedef struct bp_data_time { /* Call time */ struct bp_data *next; struct bp_data *prev; BeamInstr orig_instr; + BeamInstr this_instr; /* key */ Uint pause; Uint n; bp_time_hash_t *hash; -- cgit v1.2.3 From ff9531eb5e6aaa5a4802db0db5e0e850f4233702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 6 May 2010 20:18:21 +0200 Subject: Add a scheduler array for BpData at BeamInstr[-4] To solve the issue of multiple schedulers constantly updating the head pointer to the bp data wheel, each scheduler now has its own entrypoint to the wheel. This head pointer can be updated without a locking being taken. Previously there were no lock ... --- erts/emulator/beam/beam_bp.c | 150 ++++++++++++++++++++++++++---------------- erts/emulator/beam/beam_bp.h | 108 +++++++++++++++++++----------- erts/emulator/beam/beam_emu.c | 37 +++++++---- erts/emulator/beam/erl_nif.c | 3 +- 4 files changed, 190 insertions(+), 108 deletions(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 7b46f073c6..79d150c9e2 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -101,6 +101,9 @@ do { \ (b)->prev = (a); \ } while (0) + + + /* ************************************************************************* ** Local prototypes */ @@ -127,7 +130,7 @@ static int clear_function_break(Module *modp, BeamInstr *pc, BeamInstr break_op); static BpData *is_break(BeamInstr *pc, BeamInstr break_op); -static BpData *get_break(BeamInstr *pc, BeamInstr break_op); +static BpData *get_break(Process *p, BeamInstr *pc, BeamInstr break_op); /* bp_hash */ #define BP_TIME_ADD(pi0, pi1) \ @@ -296,9 +299,12 @@ BeamInstr erts_trace_break(Process *p, BeamInstr *pc, Eterm *args, Uint32 *ret_flags, Eterm *tracer_pid) { Eterm tpid1, tpid2; - BpDataTrace *bdt = (BpDataTrace *) pc[-4]; + BpData **bds = (BpData **) (pc)[-4]; + BpDataTrace *bdt = NULL; + ASSERT(bds); ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); + bdt = (BpDataTrace *) bds[bp_sched2ix_proc(p)]; ASSERT(bdt); bdt = (BpDataTrace *) bdt->next; ASSERT(bdt); @@ -317,7 +323,7 @@ erts_trace_break(Process *p, BeamInstr *pc, Eterm *args, bdt->tracer_pid = tpid2; ErtsSmpBPUnlock(bdt); } - pc[-4] = (BeamInstr) bdt; + bds[bp_sched2ix_proc(p)] = (BpData *) bdt; return bdt->orig_instr; } @@ -329,12 +335,15 @@ erts_trace_break(Process *p, BeamInstr *pc, Eterm *args, Uint32 erts_bif_mtrace(Process *p, BeamInstr *pc, Eterm *args, int local, Eterm *tracer_pid) { - BpDataTrace *bdt = (BpDataTrace *) pc[-4]; + BpData **bds = (BpData **) (pc)[-4]; + BpDataTrace *bdt = NULL; + ASSERT(tracer_pid); - if (bdt) { + if (bds) { Eterm tpid1, tpid2; Uint32 flags; + bdt = (BpDataTrace *)bds[bp_sched2ix_proc(p)]; ErtsSmpBPLock(bdt); tpid1 = tpid2 = bdt->tracer_pid; @@ -520,7 +529,15 @@ erts_find_local_func(Eterm mfa[3]) { } /* bp_hash */ - +ERTS_INLINE Uint bp_sched2ix() { +#ifdef ERTS_SMP + ErtsSchedulerData *esdp; + esdp = erts_get_scheduler_data(); + return esdp->no - 1; +#else + return 0; +#endif +} static void bp_hash_init(bp_time_hash_t *hash, Uint n) { Uint size = sizeof(bp_data_time_item_t)*n; Uint i; @@ -528,7 +545,6 @@ static void bp_hash_init(bp_time_hash_t *hash, Uint n) { hash->n = n; hash->used = 0; - hash->item = (bp_data_time_item_t *)Alloc(size); sys_memzero(hash->item, size); @@ -654,7 +670,6 @@ static void bp_time_diff(bp_data_time_item_t *item, /* out */ void erts_schedule_time_break(Process *p, Uint schedule) { Uint ms, s, us; process_breakpoint_time_t *pbt = NULL; - Uint ix = 0; bp_data_time_item_t sitem, *item = NULL; bp_time_hash_t *h = NULL; BpDataTime *pbdt = NULL; @@ -663,18 +678,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) { pbt = ERTS_PROC_GET_CALL_TIME(p); -#ifdef ERTS_SMP - ix = p->scheduler_data->no - 1; -#else - ix = 0; -#endif -/* - ASSERT( (p->status == P_RUNNING) || - (p->status == P_WAITING) || - (p->status == P_RUNABLE)); -*/ if (pbt) { - get_sys_now(&ms,&s,&us); switch(schedule) { case ERTS_BP_CALL_TIME_SCHEDULE_EXITING : @@ -685,13 +689,14 @@ void erts_schedule_time_break(Process *p, Uint schedule) { * the previous breakpoint. */ - pbdt = (BpDataTime *) get_break(pbt->pc, (Uint) BeamOp(op_i_time_breakpoint)); + pbdt = (BpDataTime *) get_break(p, pbt->pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); if (pbdt) { + get_sys_now(&ms,&s,&us); bp_time_diff(&sitem, pbt, ms, s, us); sitem.pid = p->id; sitem.count = 0; - h = &(pbdt->hash[ix]); + h = &(pbdt->hash[bp_sched2ix_proc(p)]); ASSERT(h); ASSERT(h->item); @@ -709,6 +714,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) { * timestamp it and remove the previous * timestamp in the psd. */ + get_sys_now(&ms,&s,&us); pbt->ms = ms; pbt->s = s; pbt->us = us; @@ -718,14 +724,6 @@ void erts_schedule_time_break(Process *p, Uint schedule) { /* will never happen */ break; } -#ifdef DEBUG - } else { - /* if pbt is null, then the process has just been spawned - * and status should be runnable. - */ - ASSERT( (p->status == P_RUNABLE) || - (p->status == P_WAITING)); -#endif } /* pbt */ } @@ -745,7 +743,6 @@ void erts_schedule_time_break(Process *p, Uint schedule) { void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type) { Uint ms,s,us; process_breakpoint_time_t *pbt = NULL; - int ix = 0; bp_data_time_item_t sitem, *item = NULL; bp_time_hash_t *h = NULL; BpDataTime *pbdt = NULL; @@ -759,12 +756,6 @@ void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type pbt = ERTS_PROC_GET_CALL_TIME(p); get_sys_now(&ms,&s,&us); -#ifdef ERTS_SMP - ix = p->scheduler_data->no - 1; -#else - ix = 0; -#endif - switch(type) { /* get pbt * timestamp = t0 @@ -783,11 +774,11 @@ void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type sitem.count = 0; /* previous breakpoint */ - pbdt = (BpDataTime *) get_break(pbt->pc, (Uint) BeamOp(op_i_time_breakpoint)); + pbdt = (BpDataTime *) get_break(p, pbt->pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); /* if null then the breakpoint was removed */ if (pbdt) { - h = &(pbdt->hash[ix]); + h = &(pbdt->hash[bp_sched2ix_proc(p)]); ASSERT(h); ASSERT(h->item); @@ -813,7 +804,7 @@ void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type /* this breakpoint */ ASSERT(bdt); - h = &(bdt->hash[ix]); + h = &(bdt->hash[bp_sched2ix_proc(p)]); ASSERT(h); ASSERT(h->item); @@ -851,11 +842,11 @@ void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type sitem.count = 0; /* previous breakpoint */ - pbdt = (BpDataTime *) get_break(pbt->pc, (Uint) BeamOp(op_i_time_breakpoint)); + pbdt = (BpDataTime *) get_break(p, pbt->pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); + /* beware, the trace_pattern might have been removed */ if (pbdt) { - - h = &(pbdt->hash[ix]); + h = &(pbdt->hash[bp_sched2ix_proc(p)]); ASSERT(h); ASSERT(h->item); @@ -874,6 +865,10 @@ void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type pbt->us = us; } break; + default : + ASSERT(0); + /* will never happen */ + break; } } @@ -946,8 +941,9 @@ static int set_module_break(Module *modp, Eterm mfa[3], int specified, static int set_function_break(Module *modp, BeamInstr *pc, Binary *match_spec, BeamInstr break_op, enum erts_break_op count_op, Eterm tracer_pid) { - BpData *bd, **r; + BpData *bd, **r, ***rs; size_t size; + Uint ix = 0; BeamInstr **code_base = (BeamInstr **)modp->code; ASSERT(code_base); @@ -1037,7 +1033,15 @@ static int set_function_break(Module *modp, BeamInstr *pc, size = sizeof(BpDataDebug); } } - r = (BpData **) (pc-4); + rs = (BpData ***) (pc-4); + if (! *rs) { + size_t ssize = sizeof(BeamInstr) * erts_no_schedulers; + *rs = (BpData **) Alloc(ssize); + sys_memzero(*rs, ssize); + } + + r = &((*rs)[0]); + if (! *r) { ASSERT(*pc != (BeamInstr) BeamOp(op_i_trace_breakpoint)); ASSERT(*pc != (BeamInstr) BeamOp(op_i_mtrace_breakpoint)); @@ -1058,12 +1062,12 @@ static int set_function_break(Module *modp, BeamInstr *pc, if (*pc == (BeamInstr) BeamOp(op_i_debug_breakpoint)) { /* Debug bp must be last, so if it is also first; * it must be singleton. */ - ASSERT(BpSingleton(*r)); + ASSERT(BpSingleton(*r)); /* Insert new bp first in the ring, i.e second to last. */ bd = Alloc(size); BpInitAndSpliceNext(bd, *pc, *r); *pc = break_op; - } else if ((*r)->prev->orig_instr + } else if ((*r)->prev->orig_instr == (BeamInstr) BeamOp(op_i_debug_breakpoint)) { /* Debug bp last in the ring; insert new second to last. */ bd = Alloc(size); @@ -1077,6 +1081,10 @@ static int set_function_break(Module *modp, BeamInstr *pc, *r = bd; } } + for (ix = 1; ix < erts_no_schedulers; ++ix) { + (*rs)[ix] = (*rs)[0]; + } + bd->this_instr = break_op; /* Init the bp type specific data */ if (break_op == (BeamInstr) BeamOp(op_i_trace_breakpoint) || @@ -1161,6 +1169,7 @@ static int clear_module_break(Module *m, Eterm mfa[3], int specified, static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { BpData *bd; + Uint ix = 0; BeamInstr **code_base = (BeamInstr **)m->code; ASSERT(code_base); @@ -1178,8 +1187,17 @@ static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { * but break_op may be 0 which matches any type. */ BeamInstr op; - BpData **r = (BpData **) (pc-4); + BpData ***rs = (BpData ***) (pc - 4); + BpData **r = NULL; + +#ifdef DEBUG + for (ix = 1; ix < erts_no_schedulers; ++ix) { + ASSERT((*rs)[ix] == (*rs)[0]); + } +#endif + r = &((*rs)[0]); + ASSERT(*r); /* Find opcode for this breakpoint */ if (break_op) { @@ -1195,8 +1213,9 @@ static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { if (BpSingleton(bd)) { ASSERT(*r == bd); /* Only one breakpoint to remove */ - *r = NULL; *pc = bd->orig_instr; + Free(*rs); + *rs = NULL; } else { BpData *bd_prev = bd->prev; @@ -1256,7 +1275,12 @@ static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { Free(bd); ASSERT(((BeamInstr) code_base[MI_NUM_BREAKPOINTS]) > 0); --(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]); - } + if (*rs) { + for (ix = 1; ix < erts_no_schedulers; ++ix) { + (*rs)[ix] = (*rs)[0]; + } + } + } /* while bd != NULL */ return 1; } @@ -1272,7 +1296,16 @@ static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { static BpData *is_break(BeamInstr *pc, BeamInstr break_op) { ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); if (! erts_is_native_break(pc)) { - BpData *bd = (BpData *) pc[-4]; + BpData **rs = (BpData **) pc[-4]; + BpData *bd = NULL, *ebd = NULL; + + if (! rs) { + return NULL; + } + + bd = ebd = rs[bp_sched2ix()]; + + ASSERT(bd); if (break_op == 0) { return bd; @@ -1285,7 +1318,7 @@ static BpData *is_break(BeamInstr *pc, BeamInstr break_op) { return NULL; } bd = bd->next; - while (bd != (BpData *) pc[-4]) { + while (bd != ebd) { ASSERT(bd); if (bd->orig_instr == break_op) { bd = bd->next; @@ -1298,17 +1331,24 @@ static BpData *is_break(BeamInstr *pc, BeamInstr break_op) { } return NULL; } -static BpData *get_break(BeamInstr *pc, BeamInstr break_op) { +static BpData *get_break(Process *p, BeamInstr *pc, BeamInstr break_op) { ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); if (! erts_is_native_break(pc)) { - BpData *bd = (BpData *) pc[-4]; + BpData **rs = (BpData **) pc[-4]; + BpData *bd = NULL, *ebd = NULL; - if (! bd){ + if (! rs) { return NULL; } + bd = ebd = rs[bp_sched2ix_proc(p)]; + ASSERT(bd); + if (bd->this_instr == break_op) { + return bd; + } + bd = bd->next; - while (bd != (BpData *) pc[-4]) { + while (bd != ebd) { ASSERT(bd); if (bd->this_instr == break_op) { ASSERT(bd); diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index 6b339401fc..b76e0c01e7 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -27,24 +27,41 @@ -/* -** Common struct to all bp_data_* -** -** Two gotchas: -** -** 1) The type of bp_data structure in the ring is deduced from the -** orig_instr field of the structure _before_ in the ring, except for -** the first structure in the ring that has its instruction in -** pc[0] of the code to execute. -** -** 2) pc[-4] points to the _last_ structure in the ring before the -** breakpoints are being executed. -** -** So, as an example, when a breakpointed function starts to execute, -** the first instruction that is a breakpoint instruction at pc[0] finds -** its data at ((BpData *) pc[-4])->next and has to cast that pointer -** to the correct bp_data type. +/* A couple of gotchas: + * + * The breakpoint structure from BeamInstr, + * In beam_emu where the instruction counter pointer, I (or pc), + * points to the *current* instruction. At that time, if the instruction + * is a breakpoint instruction the pc looks like the following, + * + * I[-5] | op_i_func_info_IaaI | scheduler specific entries + * I[-4] | BpData** bpa | --> | BpData * bdas1 | ... | BpData * bdasN | + * I[-3] | Tagged Module | | | + * I[-2] | Tagged Function | V V + * I[-1] | Arity | BpData -> BpData -> BpData -> BpData + * I[0] | The bp instruction | ^ * the bp wheel * | + * |------------------------------ + * + * Common struct to all bp_data_* + * + * 1) The type of bp_data structure in the ring is deduced from the + * orig_instr field of the structure _before_ in the ring, except for + * the first structure in the ring that has its instruction in + * pc[0] of the code to execute. + * This is valid as long as you don't search for the function while it is + * being executed by something else. Or is in the middle of its rotation for + * any other reason. + * A key, the bp beam instruction, is included for this reason. + * + * 2) pc[-4][sched_id - 1] points to the _last_ structure in the ring before the + * breakpoints are being executed. + * + * So, as an example, when a breakpointed function starts to execute, + * the first instruction that is a breakpoint instruction at pc[0] finds + * its data at ((BpData **) pc[-4][sched_id - 1])->next and has to cast that pointer + * to the correct bp_data type. */ + typedef struct bp_data { struct bp_data *next; /* Doubly linked ring pointers */ struct bp_data *prev; /* -"- */ @@ -127,31 +144,46 @@ extern erts_smp_spinlock_t erts_bp_lock; #define ErtsSmpBPUnlock(BDC) #endif -#define ErtsCountBreak(pc,instr_result) \ -do { \ - BpDataCount *bdc = (BpDataCount *) (pc)[-4]; \ - \ +ERTS_INLINE Uint bp_sched2ix(void); + +#ifdef ERTS_SMP +#define bp_sched2ix_proc(p) ((p)->scheduler_data->no - 1) +#else +#define bp_sched2ix_proc(p) (0) +#endif + +#define ErtsCountBreak(pc,instr_result) \ +do { \ + BpData **bds = (BpData **) (pc)[-4]; \ + BpDataCount *bdc = NULL; \ + Uint ix = bp_sched2ix(); \ + \ ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \ - ASSERT(bdc); \ - bdc = (BpDataCount *) bdc->next; \ - ASSERT(bdc); \ - (pc)[-4] = (BeamInstr) bdc; \ - ErtsSmpBPLock(bdc); \ - if (bdc->count >= 0) bdc->count++; \ - ErtsSmpBPUnlock(bdc); \ - *(instr_result) = bdc->orig_instr; \ + ASSERT(bds); \ + bdc = (BpDataCount *) bds[ix]; \ + bdc = (BpDataCount *) bdc->next; \ + ASSERT(bdc); \ + bds[ix] = (BpData *) bdc; \ + ErtsSmpBPLock(bdc); \ + if (bdc->count >= 0) bdc->count++; \ + ErtsSmpBPUnlock(bdc); \ + *(instr_result) = bdc->orig_instr; \ } while (0) -#define ErtsBreakSkip(pc,instr_result) \ -do { \ - BpData *bd = (BpData *) (pc)[-4]; \ - \ +#define ErtsBreakSkip(pc,instr_result) \ +do { \ + BpData **bds = (BpData **) (pc)[-4]; \ + BpData *bd = NULL; \ + Uint ix = bp_sched2ix(); \ + \ ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \ - ASSERT(bd); \ - bd = bd->next; \ - ASSERT(bd); \ - (pc)[-4] = (BeamInstr) bd; \ - *(instr_result) = bd->orig_instr; \ + ASSERT(bds); \ + bd = bds[ix]; \ + ASSERT(bd); \ + bd = bd->next; \ + ASSERT(bd); \ + bds[ix] = bd; \ + *(instr_result) = bd->orig_instr; \ } while (0) enum erts_break_op{ diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 29d0c0afde..6313319b9b 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4415,23 +4415,28 @@ apply_bif_or_nif_epilogue: OpCase(i_time_breakpoint): { BeamInstr real_I; - BpDataTime *bdt = (BpDataTime *) (I)[-4]; - Uint tail_call = 0; + BpData **bds = (BpData **) (I)[-4]; + BpDataTime *bdt = NULL; + Uint ix = 0; +#ifdef ERTS_SMP + ix = c_p->scheduler_data->no - 1; +#else + ix = 0; +#endif + bdt = (BpDataTime *)bds[ix]; ASSERT((I)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); ASSERT(bdt); bdt = (BpDataTime *) bdt->next; ASSERT(bdt); - (I)[-4] = (BeamInstr) bdt; + bds[ix] = (BpData *) bdt; real_I = bdt->orig_instr; ASSERT(VALID_INSTR(real_I)); if (IS_TRACED_FL(c_p, F_TRACE_CALLS) && !(bdt->pause)) { - if (*cp_val((Eterm)c_p->cp) == (BeamInstr) OpCode(i_return_time_trace)) { - tail_call = 1; - } - - if (tail_call) { + if ( (*(c_p->cp) == (BeamInstr) OpCode(i_return_time_trace)) || + (*(c_p->cp) == (BeamInstr) OpCode(return_trace)) || + (*(c_p->cp) == (BeamInstr) OpCode(i_return_to_trace))) { /* This _IS_ a tail recursive call */ SWAPOUT; erts_trace_time_break(c_p, I, bdt, ERTS_BP_CALL_TIME_TAIL_CALL); @@ -4458,7 +4463,7 @@ apply_bif_or_nif_epilogue: E -= 2; E[0] = make_cp(I); E[1] = make_cp(c_p->cp); /* original return address */ - c_p->cp = (Eterm *) make_cp(beam_return_time_trace); + c_p->cp = (BeamInstr *) make_cp(beam_return_time_trace); } } @@ -4488,18 +4493,20 @@ apply_bif_or_nif_epilogue: BeamInstr real_I; Uint32 flags; Eterm tracer_pid; - Uint *cpp; + BeamInstr *cpp; int return_to_trace = 0, need = 0; flags = 0; SWAPOUT; reg[0] = r(0); if (*(c_p->cp) == (BeamInstr) OpCode(return_trace)) { - cpp = (Uint*)&E[2]; - } else if (*(c_p->cp) - == (BeamInstr) OpCode(i_return_to_trace)) { + cpp = (BeamInstr*)&E[2]; + } else if (*(c_p->cp) == (BeamInstr) OpCode(i_return_to_trace)) { + return_to_trace = !0; + cpp = (BeamInstr*)&E[0]; + } else if (*(c_p->cp) == (BeamInstr) OpCode(i_return_time_trace)) { return_to_trace = !0; - cpp = (Uint*)&E[0]; + cpp = (BeamInstr*)&E[0]; } else { cpp = NULL; } @@ -4516,6 +4523,8 @@ apply_bif_or_nif_epilogue: } else if (*cp_val(*cpp) == (BeamInstr) OpCode(i_return_to_trace)) { return_to_trace = !0; cpp += 1; + } else if (*cp_val(*cpp) == (BeamInstr) OpCode(i_return_time_trace)) { + cpp += 2; } else break; } diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 75de00d4c1..3d63fa1caf 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1546,7 +1546,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) code_ptr[5+0] = (BeamInstr) BeamOp(op_call_nif); } else { /* Function traced, patch the original instruction word */ - BpData* bp = (BpData*) code_ptr[1]; + BpData** bps = (BpData**) code_ptr[1]; + BpData* bp = (BpData*) bps[bp_sched2ix()]; bp->orig_instr = (BeamInstr) BeamOp(op_call_nif); } code_ptr[5+1] = (BeamInstr) entry->funcs[i].fptr; -- cgit v1.2.3 From 96a6c45d081e41dbe2d44a8d07d78b46d0e9f587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 11 May 2010 20:14:09 +0200 Subject: Teach call count tracing to use atomics Call count previously used a global lock for accessing and writing its counter in the breakpoint. This is now changed to atomics instead. The change will let call count tracing and cprof to scale better when increasing the number of schedulers. --- erts/emulator/beam/beam_bp.c | 27 +++++++++++++++------------ erts/emulator/beam/beam_bp.h | 16 ++++++++-------- erts/emulator/beam/beam_emu.c | 4 ++-- 3 files changed, 25 insertions(+), 22 deletions(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 79d150c9e2..424e3324f9 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -439,9 +439,7 @@ erts_is_count_break(BeamInstr *pc, Sint *count_ret) { if (bdc) { if (count_ret) { - ErtsSmpBPLock(bdc); - *count_ret = bdc->count; - ErtsSmpBPUnlock(bdc); + *count_ret = (Sint) erts_smp_atomic_read(&bdc->acount); } return !0; } @@ -976,16 +974,23 @@ static int set_function_break(Module *modp, BeamInstr *pc, ASSERT(is_nil(tracer_pid)); if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) { BpDataCount *bdc = (BpDataCount *) bd; + long count = 0; + long res = 0; - ErtsSmpBPLock(bdc); if (count_op == erts_break_stop) { - if (bdc->count >= 0) { - bdc->count = -bdc->count-1; /* Stop call counter */ + count = erts_smp_atomic_read(&bdc->acount); + if (count >= 0) { + while(1) { + res = erts_smp_atomic_cmpxchg(&bdc->acount, -count - 1, count); + if ((res == count) || count < 0) break; + count = res; + } } } else { - bdc->count = 0; /* Reset call counter */ + /* Reset call counter */ + erts_smp_atomic_set(&bdc->acount, 0); } - ErtsSmpBPUnlock(bdc); + } else if (break_op == (Uint) BeamOp(op_i_time_breakpoint)) { BpDataTime *bdt = (BpDataTime *) bd; Uint i = 0; @@ -1015,8 +1020,7 @@ static int set_function_break(Module *modp, BeamInstr *pc, ASSERT(! match_spec); ASSERT(is_nil(tracer_pid)); if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) { - if (count_op == erts_break_reset - || count_op == erts_break_stop) { + if (count_op == erts_break_reset || count_op == erts_break_stop) { /* Do not insert a new breakpoint */ return 1; } @@ -1108,8 +1112,7 @@ static int set_function_break(Module *modp, BeamInstr *pc, } } else if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) { BpDataCount *bdc = (BpDataCount *) bd; - - bdc->count = 0; + erts_smp_atomic_init(&bdc->acount, 0); } ++(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]); return 1; diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index b76e0c01e7..d22dd5de87 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -93,7 +93,7 @@ typedef struct bp_data_count { /* Call count */ struct bp_data *prev; BeamInstr orig_instr; BeamInstr this_instr; /* key */ - Sint count; + erts_smp_atomic_t acount; } BpDataCount; typedef struct { @@ -152,11 +152,12 @@ ERTS_INLINE Uint bp_sched2ix(void); #define bp_sched2ix_proc(p) (0) #endif -#define ErtsCountBreak(pc,instr_result) \ +#define ErtsCountBreak(p, pc,instr_result) \ do { \ BpData **bds = (BpData **) (pc)[-4]; \ BpDataCount *bdc = NULL; \ - Uint ix = bp_sched2ix(); \ + Uint ix = bp_sched2ix_proc( (p) ); \ + long count = 0; \ \ ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \ ASSERT(bds); \ @@ -164,17 +165,16 @@ do { \ bdc = (BpDataCount *) bdc->next; \ ASSERT(bdc); \ bds[ix] = (BpData *) bdc; \ - ErtsSmpBPLock(bdc); \ - if (bdc->count >= 0) bdc->count++; \ - ErtsSmpBPUnlock(bdc); \ + count = erts_smp_atomic_read(&bdc->acount); \ + if (count >= 0) erts_smp_atomic_inc(&bdc->acount); \ *(instr_result) = bdc->orig_instr; \ } while (0) -#define ErtsBreakSkip(pc,instr_result) \ +#define ErtsBreakSkip(p, pc,instr_result) \ do { \ BpData **bds = (BpData **) (pc)[-4]; \ BpData *bd = NULL; \ - Uint ix = bp_sched2ix(); \ + Uint ix = bp_sched2ix_proc( (p) ); \ \ ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \ ASSERT(bds); \ diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 6313319b9b..d543006b62 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4404,7 +4404,7 @@ apply_bif_or_nif_epilogue: OpCase(i_count_breakpoint): { BeamInstr real_I; - ErtsCountBreak((BeamInstr *) I, &real_I); + ErtsCountBreak(c_p, (BeamInstr *) I, &real_I); ASSERT(VALID_INSTR(real_I)); Goto(real_I); } @@ -4485,7 +4485,7 @@ apply_bif_or_nif_epilogue: if (! IS_TRACED_FL(c_p, F_TRACE_CALLS)) { BeamInstr real_I; - ErtsBreakSkip((BeamInstr *) I, &real_I); + ErtsBreakSkip(c_p, (BeamInstr *) I, &real_I); Goto(real_I); } /* Fall through to next case */ -- cgit v1.2.3 From c523cc520d7de33e97be6da77acdd35033efdc41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 24 May 2010 16:09:17 +0200 Subject: Fix BeamInstr code array in export.h --- erts/emulator/beam/export.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h index 87c1d483f4..c604fdf7c3 100644 --- a/erts/emulator/beam/export.h +++ b/erts/emulator/beam/export.h @@ -37,7 +37,7 @@ typedef struct export void* address; /* Pointer to code for function. */ struct binary* match_prog_set; /* Match program for tracing. */ - Eterm fake_op_func_info_for_hipe[2]; /* MUST be just before code[] */ + BeamInstr fake_op_func_info_for_hipe[2]; /* MUST be just before code[] */ /* * code[0]: Tagged atom for module. * code[1]: Tagged atom for function. @@ -52,11 +52,7 @@ typedef struct export * on_load function that has not been run yet. * Otherwise: 0. */ -#if HALFWORD_HEAP BeamInstr code[5]; -#else - Eterm code[5]; -#endif } Export; -- cgit v1.2.3 From b2369c68b33344eb44544c0892cffb8acc658ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 25 May 2010 14:17:07 +0200 Subject: Teach call time trace patterns to include bifs --- erts/emulator/beam/beam_bp.c | 209 ++++++++++++++++++------------------- erts/emulator/beam/beam_bp.h | 5 + erts/emulator/beam/beam_emu.c | 4 +- erts/emulator/beam/erl_bif_trace.c | 20 +++- erts/emulator/beam/erl_trace.c | 26 ++++- erts/emulator/beam/global.h | 18 ++-- 6 files changed, 158 insertions(+), 124 deletions(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 424e3324f9..9004c74f51 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -102,6 +102,8 @@ do { \ } while (0) +#define BREAK_IS_BIF (1) +#define BREAK_IS_ERL (0) /* ************************************************************************* @@ -118,7 +120,7 @@ static int set_break(Eterm mfa[3], int specified, static int set_module_break(Module *modp, Eterm mfa[3], int specified, Binary *match_spec, BeamInstr break_op, enum erts_break_op count_op, Eterm tracer_pid); -static int set_function_break(Module *modp, BeamInstr *pc, +static int set_function_break(Module *modp, BeamInstr *pc, int bif, Binary *match_spec, BeamInstr break_op, enum erts_break_op count_op, Eterm tracer_pid); @@ -126,7 +128,7 @@ static int clear_break(Eterm mfa[3], int specified, BeamInstr break_op); static int clear_module_break(Module *modp, Eterm mfa[3], int specified, BeamInstr break_op); -static int clear_function_break(Module *modp, BeamInstr *pc, +static int clear_function_break(Module *modp, BeamInstr *pc, int bif, BeamInstr break_op); static BpData *is_break(BeamInstr *pc, BeamInstr break_op); @@ -178,25 +180,20 @@ erts_set_mtrace_break(Eterm mfa[3], int specified, Binary *match_spec, (BeamInstr) BeamOp(op_i_mtrace_breakpoint), 0, tracer_pid); } +/* set breakpoint data for on exported bif entry */ + void erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, Eterm tracer_pid) { - BpDataTrace *bdt; ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0)); + set_function_break(NULL, pc, BREAK_IS_BIF, match_spec, (BeamInstr) BeamOp(op_i_mtrace_breakpoint), 0, tracer_pid); +} - bdt = (BpDataTrace *) pc[-4]; - if (bdt) { - MatchSetUnref(bdt->match_spec); - MatchSetRef(match_spec); - bdt->match_spec = match_spec; - bdt->tracer_pid = tracer_pid; - } else { - bdt = Alloc(sizeof(BpDataTrace)); - BpInit((BpData *) bdt, 0); - MatchSetRef(match_spec); - bdt->match_spec = match_spec; - bdt->tracer_pid = tracer_pid; - pc[-4] = (BeamInstr) bdt; - } +void erts_set_time_trace_bif(BeamInstr *pc, enum erts_break_op count_op) { + set_function_break(NULL, pc, BREAK_IS_BIF, NULL, (BeamInstr) BeamOp(op_i_time_breakpoint), count_op, NIL); +} + +void erts_clear_time_trace_bif(BeamInstr *pc) { + clear_function_break(NULL, pc, BREAK_IS_BIF, (BeamInstr) BeamOp(op_i_time_breakpoint)); } int @@ -236,17 +233,7 @@ erts_clear_mtrace_break(Eterm mfa[3], int specified) { void erts_clear_mtrace_bif(BeamInstr *pc) { - BpDataTrace *bdt; - ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0)); - - bdt = (BpDataTrace *) pc[-4]; - if (bdt) { - if (bdt->match_spec) { - MatchSetUnref(bdt->match_spec); - } - Free(bdt); - } - pc[-4] = (BeamInstr) NULL; + clear_function_break(NULL, pc, BREAK_IS_BIF, (BeamInstr) BeamOp(op_i_mtrace_breakpoint)); } int @@ -287,7 +274,7 @@ int erts_clear_function_break(Module *modp, BeamInstr *pc) { ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0)); ASSERT(modp); - return clear_function_break(modp, pc, 0); + return clear_function_break(modp, pc, BREAK_IS_ERL, 0); } @@ -403,24 +390,6 @@ erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret, Eterm *tracer_pid_r return 0; } -int -erts_is_mtrace_bif(BeamInstr *pc, Binary **match_spec_ret, Eterm *tracer_pid_ret) { - BpDataTrace *bdt = (BpDataTrace *) pc[-4]; - - if (bdt) { - if (match_spec_ret) { - *match_spec_ret = bdt->match_spec; - } - if (tracer_pid_ret) { - ErtsSmpBPLock(bdt); - *tracer_pid_ret = bdt->tracer_pid; - ErtsSmpBPUnlock(bdt); - } - return !0; - } - return 0; -} - int erts_is_native_break(BeamInstr *pc) { #ifdef HIPE @@ -447,12 +416,12 @@ erts_is_count_break(BeamInstr *pc, Sint *count_ret) { } int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *retval) { - BpDataTime *bdt = (BpDataTime *) is_break(pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); Uint i, ix; bp_time_hash_t hash; Uint size; Eterm *hp, t; bp_data_time_item_t *item = NULL; + BpDataTime *bdt = (BpDataTime *) is_break(pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); if (bdt) { if (retval) { @@ -475,8 +444,8 @@ int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *retval) { } } } + /* *retval should be NIL or term from previous bif in export entry */ - *retval = NIL; if (hash.used > 0) { size = (5 + 2)*hash.used; hp = HAlloc(p, size); @@ -926,27 +895,35 @@ static int set_module_break(Module *modp, Eterm mfa[3], int specified, ASSERT(code_ptr[0] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); if ((specified < 2 || mfa[1] == ((Eterm) code_ptr[3])) && (specified < 3 || ((int) mfa[2]) == ((int) code_ptr[4]))) { - BeamInstr *pc = code_ptr+5; + BeamInstr *pc = code_ptr+5; num_processed += - set_function_break(modp, pc, match_spec, + set_function_break(modp, pc, BREAK_IS_ERL, match_spec, break_op, count_op, tracer_pid); } } return num_processed; } -static int set_function_break(Module *modp, BeamInstr *pc, +static int set_function_break(Module *modp, BeamInstr *pc, int bif, Binary *match_spec, BeamInstr break_op, enum erts_break_op count_op, Eterm tracer_pid) { + + BeamInstr **code_base = NULL; BpData *bd, **r, ***rs; size_t size; Uint ix = 0; - BeamInstr **code_base = (BeamInstr **)modp->code; - ASSERT(code_base); - ASSERT(code_base <= (BeamInstr **)pc); - ASSERT((BeamInstr **)pc < code_base + (modp->code_length/sizeof(BeamInstr *))); + if (bif == BREAK_IS_ERL) { + code_base = (BeamInstr **)modp->code; + ASSERT(code_base); + ASSERT(code_base <= (BeamInstr **)pc); + ASSERT((BeamInstr **)pc < code_base + (modp->code_length/sizeof(BeamInstr *))); + } else { + ASSERT(*pc == (BeamInstr) em_apply_bif); + ASSERT(modp == NULL); + } + /* * Currently no trace support for native code. */ @@ -970,13 +947,14 @@ static int set_function_break(Module *modp, BeamInstr *pc, ErtsSmpBPUnlock(bdt); MatchSetUnref(old_match_spec); } else { + BpDataCount *bdc = (BpDataCount *) bd; + long count = 0; + long res = 0; + ASSERT(! match_spec); ASSERT(is_nil(tracer_pid)); - if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) { - BpDataCount *bdc = (BpDataCount *) bd; - long count = 0; - long res = 0; + if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) { if (count_op == erts_break_stop) { count = erts_smp_atomic_read(&bdc->acount); if (count >= 0) { @@ -991,7 +969,7 @@ static int set_function_break(Module *modp, BeamInstr *pc, erts_smp_atomic_set(&bdc->acount, 0); } - } else if (break_op == (Uint) BeamOp(op_i_time_breakpoint)) { + } else if (break_op == (BeamInstr) BeamOp(op_i_time_breakpoint)) { BpDataTime *bdt = (BpDataTime *) bd; Uint i = 0; @@ -1055,8 +1033,10 @@ static int set_function_break(Module *modp, BeamInstr *pc, /* First breakpoint; create singleton ring */ bd = Alloc(size); BpInit(bd, *pc); - *pc = break_op; *r = bd; + if (bif == BREAK_IS_ERL) { + *pc = break_op; + } } else { ASSERT(*pc == (BeamInstr) BeamOp(op_i_trace_breakpoint) || *pc == (BeamInstr) BeamOp(op_i_mtrace_breakpoint) || @@ -1070,7 +1050,9 @@ static int set_function_break(Module *modp, BeamInstr *pc, /* Insert new bp first in the ring, i.e second to last. */ bd = Alloc(size); BpInitAndSpliceNext(bd, *pc, *r); - *pc = break_op; + if (bif == BREAK_IS_ERL) { + *pc = break_op; + } } else if ((*r)->prev->orig_instr == (BeamInstr) BeamOp(op_i_debug_breakpoint)) { /* Debug bp last in the ring; insert new second to last. */ @@ -1114,7 +1096,10 @@ static int set_function_break(Module *modp, BeamInstr *pc, BpDataCount *bdc = (BpDataCount *) bd; erts_smp_atomic_init(&bdc->acount, 0); } - ++(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]); + + if (bif == BREAK_IS_ERL) { + ++(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]); + } return 1; } @@ -1164,26 +1149,34 @@ static int clear_module_break(Module *m, Eterm mfa[3], int specified, BeamInstr *pc = code_ptr + 5; num_processed += - clear_function_break(m, pc, break_op); + clear_function_break(m, pc, BREAK_IS_ERL, break_op); } } return num_processed; } -static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { +static int clear_function_break(Module *m, BeamInstr *pc, int bif, BeamInstr break_op) { BpData *bd; Uint ix = 0; - BeamInstr **code_base = (BeamInstr **)m->code; - - ASSERT(code_base); - ASSERT(code_base <= (BeamInstr **)pc); - ASSERT((BeamInstr **)pc < code_base + (m->code_length/sizeof(BeamInstr *))); + BeamInstr **code_base = NULL; + + if (bif == BREAK_IS_ERL) { + code_base = (BeamInstr **)m->code; + ASSERT(code_base); + ASSERT(code_base <= (BeamInstr **)pc); + ASSERT((BeamInstr **)pc < code_base + (m->code_length/sizeof(BeamInstr *))); + } else { + ASSERT(*pc == (BeamInstr) em_apply_bif); + ASSERT(m == NULL); + } + /* * Currently no trace support for native code. */ if (erts_is_native_break(pc)) { return 0; } + while ( (bd = is_break(pc, break_op))) { /* Remove all breakpoints of this type. * There should be only one of each type, @@ -1216,7 +1209,9 @@ static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { if (BpSingleton(bd)) { ASSERT(*r == bd); /* Only one breakpoint to remove */ - *pc = bd->orig_instr; + if (bif == BREAK_IS_ERL) { + *pc = bd->orig_instr; + } Free(*rs); *rs = NULL; } else { @@ -1230,7 +1225,9 @@ static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { bd_prev->orig_instr = bd->orig_instr; } else if (bd_prev == *r) { /* We removed the first breakpoint in the ring */ - *pc = bd->orig_instr; + if (bif == BREAK_IS_ERL) { + *pc = bd->orig_instr; + } } else { bd_prev->orig_instr = bd->orig_instr; } @@ -1276,8 +1273,10 @@ static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { bdt->n = 0; } Free(bd); - ASSERT(((BeamInstr) code_base[MI_NUM_BREAKPOINTS]) > 0); - --(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]); + if (bif == BREAK_IS_ERL) { + ASSERT(((BeamInstr) code_base[MI_NUM_BREAKPOINTS]) > 0); + --(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]); + } if (*rs) { for (ix = 1; ix < erts_no_schedulers; ++ix) { (*rs)[ix] = (*rs)[0]; @@ -1296,7 +1295,12 @@ static int clear_function_break(Module *m, BeamInstr *pc, BeamInstr break_op) { ** returned. The program counter must point to the first executable ** (breakpoint) instruction of the function. */ -static BpData *is_break(BeamInstr *pc, BeamInstr break_op) { + +BpData *erts_get_time_break(Process *p, BeamInstr *pc) { + return get_break(p, pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); +} + +static BpData *get_break(Process *p, BeamInstr *pc, BeamInstr break_op) { ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); if (! erts_is_native_break(pc)) { BpData **rs = (BpData **) pc[-4]; @@ -1306,59 +1310,48 @@ static BpData *is_break(BeamInstr *pc, BeamInstr break_op) { return NULL; } - bd = ebd = rs[bp_sched2ix()]; - + bd = ebd = rs[bp_sched2ix_proc(p)]; ASSERT(bd); - - if (break_op == 0) { + if (bd->this_instr == break_op) { return bd; } - if (*pc == break_op) { - ASSERT(bd); - return bd->next; - } - if (! bd){ - return NULL; - } + bd = bd->next; while (bd != ebd) { ASSERT(bd); - if (bd->orig_instr == break_op) { - bd = bd->next; + if (bd->this_instr == break_op) { ASSERT(bd); return bd; - } else { - bd = bd->next; } + bd = bd->next; } } return NULL; } -static BpData *get_break(Process *p, BeamInstr *pc, BeamInstr break_op) { + +static BpData *is_break(BeamInstr *pc, BeamInstr break_op) { + BpData **rs = (BpData **) pc[-4]; + BpData *bd = NULL, *ebd = NULL; ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); - if (! erts_is_native_break(pc)) { - BpData **rs = (BpData **) pc[-4]; - BpData *bd = NULL, *ebd = NULL; - if (! rs) { - return NULL; - } + if (! rs) { + return NULL; + } - bd = ebd = rs[bp_sched2ix_proc(p)]; + bd = ebd = rs[bp_sched2ix()]; + ASSERT(bd); + if ( (break_op == 0) || (bd->this_instr == break_op)) { + return bd; + } + + bd = bd->next; + while (bd != ebd) { ASSERT(bd); if (bd->this_instr == break_op) { + ASSERT(bd); return bd; } - bd = bd->next; - while (bd != ebd) { - ASSERT(bd); - if (bd->this_instr == break_op) { - ASSERT(bd); - return bd; - } - bd = bd->next; - } } return NULL; } diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index d22dd5de87..b5d5b3c203 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -240,6 +240,11 @@ void erts_schedule_time_break(Process *p, Uint out); int erts_set_time_break(Eterm mfa[3], int specified, enum erts_break_op); int erts_clear_time_break(Eterm mfa[3], int specified); +int erts_is_time_trace_bif(Process *p, BeamInstr *pc, Eterm *call_time); +void erts_set_time_trace_bif(BeamInstr *pc, enum erts_break_op); +void erts_clear_time_trace_bif(BeamInstr *pc); +BpData *erts_get_time_break(Process *p, BeamInstr *pc); + BeamInstr *erts_find_local_func(Eterm mfa[3]); #endif /* _BEAM_BP_H */ diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index d543006b62..c0680086aa 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -231,7 +231,7 @@ BeamInstr* em_call_traced_function; BeamInstr beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */ BeamInstr beam_return_trace[1]; /* OpCode(i_return_trace) */ BeamInstr beam_exception_trace[1]; /* UGLY also OpCode(i_return_trace) */ -BeamInstr beam_return_time_trace[1]; /* OpCode(i_time_return_trace) */ +BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */ /* * All Beam instructions in numerical order. @@ -5054,6 +5054,8 @@ apply_bif_or_nif_epilogue: bif_export[i] = ep; ep->code[3] = (BeamInstr) OpCode(apply_bif); ep->code[4] = (BeamInstr) bif_table[i].f; + /* XXX: set func info for bifs */ + ((BeamInstr*)ep->code + 3)[-5] = (BeamInstr) BeamOp(op_i_func_info_IaaI); } return; diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 856669d2c9..443cac9033 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -992,10 +992,13 @@ static int function_is_traced(Process *p, r |= FUNC_TRACE_LOCAL_TRACE; *ms = ep->match_prog_set; } - if (erts_is_mtrace_bif(ep->code+3, ms_meta, + if (erts_is_mtrace_break(ep->code+3, ms_meta, tracer_pid_meta)) { r |= FUNC_TRACE_META_TRACE; } + if (erts_is_time_break(p, ep->code+3, call_time)) { + r |= FUNC_TRACE_TIME_TRACE; + } } return r ? r : FUNC_TRACE_UNTRACED; } @@ -1034,7 +1037,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) Eterm match_spec = am_false; Eterm retval = am_false; Eterm meta = am_false; - Eterm call_time; + Eterm call_time = NIL; int r; @@ -1404,6 +1407,12 @@ erts_set_trace_pattern(Eterm* mfa, int specified, erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_GLOBAL; m = 1; } + if (flags.call_time) { + erts_set_time_trace_bif(bif_export[i]->code + 3, on); + /* I don't want to remove any other tracers */ + erts_bif_trace_flags[i] |= BIF_TRACE_AS_CALL_TIME; + m = 1; + } if (erts_bif_trace_flags[i]) { setup_bif_trace(i); } @@ -1423,6 +1432,11 @@ erts_set_trace_pattern(Eterm* mfa, int specified, } m = 1; } + if (flags.call_time) { + erts_clear_time_trace_bif(bif_export[i]->code + 3); + erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_CALL_TIME; + m = 1; + } if (! erts_bif_trace_flags[i]) { reset_bif_trace(i); } @@ -1591,7 +1605,7 @@ static void reset_bif_trace(int bif_index) { ASSERT(ExportIsBuiltIn(ep)); ASSERT(ep->code[4]); ASSERT(! ep->match_prog_set); - ASSERT(! erts_is_mtrace_bif((BeamInstr *)ep->code+3, NULL, NULL)); + ASSERT(! erts_is_mtrace_break((BeamInstr *)ep->code+3, NULL, NULL)); ep->code[4] = (BeamInstr) bif_table[bif_index].f; } diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 381a182e39..3043bb1e8c 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -43,8 +43,9 @@ #undef DEBUG_PRINTOUTS #endif -extern Eterm beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */ -extern Eterm beam_return_trace[1]; /* OpCode(i_return_trace) */ +extern Eterm beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */ +extern Eterm beam_return_trace[1]; /* OpCode(i_return_trace) */ +extern Eterm beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */ /* Pseudo export entries. Never filled in with data, only used to yield unique pointers of the correct type. */ @@ -2116,6 +2117,7 @@ erts_bif_trace(int bif_index, Process* p, Uint32 flags = 0, flags_meta = 0; int global = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_GLOBAL); int local = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_LOCAL); + int time = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_CALL_TIME); Eterm meta_tracer_pid = NIL; int applying = (I == &(ep->code[3])); /* Yup, the apply code for a bif * is actually in the @@ -2139,6 +2141,17 @@ erts_bif_trace(int bif_index, Process* p, flags_meta = erts_bif_mtrace(p, ep->code+3, args, local, &meta_tracer_pid); } + if (time) { + BpDataTime *bdt = NULL; + BeamInstr *pc = (BeamInstr *)ep->code+3; + + bdt = (BpDataTime *) erts_get_time_break(p, pc); + ASSERT(bdt); + + if (!bdt->pause) { + erts_trace_time_break(p, pc, bdt, ERTS_BP_CALL_TIME_CALL); + } + } /* Restore original continuation pointer (if changed). */ p->cp = cp; @@ -2147,8 +2160,9 @@ erts_bif_trace(int bif_index, Process* p, result = func(p, arg1, arg2, arg3, I); if (applying && (flags & MATCH_SET_RETURN_TO_TRACE)) { - Uint i_return_trace = beam_return_trace[0]; - Uint i_return_to_trace = beam_return_to_trace[0]; + BeamInstr i_return_trace = beam_return_trace[0]; + BeamInstr i_return_to_trace = beam_return_to_trace[0]; + BeamInstr i_return_time_trace = beam_return_time_trace[0]; Eterm *cpp; /* Maybe advance cp to skip trace stack frames */ for (cpp = p->stop; ; cp = cp_val(*cpp++)) { @@ -2156,6 +2170,10 @@ erts_bif_trace(int bif_index, Process* p, /* Skip stack frame variables */ while (is_not_CP(*cpp)) cpp++; cpp += 2; /* Skip return_trace parameters */ + } else if (*cp == i_return_time_trace) { + /* Skip stack frame variables */ + while (is_not_CP(*cpp)) cpp++; + cpp += 1; /* Skip return_time_trace parameters */ } else if (*cp == i_return_to_trace) { /* A return_to trace message is going to be generated * by normal means, so we do not have to. diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index b042f14a7b..b4a7a22082 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1788,18 +1788,20 @@ extern void erts_match_prog_foreach_offheap(Binary *b, void (*)(ErlOffHeap *, void *), void *); -#define MATCH_SET_RETURN_TRACE 0x1 /* return trace requested */ -#define MATCH_SET_RETURN_TO_TRACE 0x2 /* Misleading name, it is not actually - set by the match program, but by the - breakpoint functions */ -#define MATCH_SET_EXCEPTION_TRACE 0x4 /* exception trace requested */ +#define MATCH_SET_RETURN_TRACE (0x1) /* return trace requested */ +#define MATCH_SET_RETURN_TO_TRACE (0x2) /* Misleading name, it is not actually + set by the match program, but by the + breakpoint functions */ +#define MATCH_SET_EXCEPTION_TRACE (0x4) /* exception trace requested */ #define MATCH_SET_RX_TRACE (MATCH_SET_RETURN_TRACE|MATCH_SET_EXCEPTION_TRACE) /* * Flag values when tracing bif + * Future note: flag field is 8 bits */ -#define BIF_TRACE_AS_LOCAL 0x1 -#define BIF_TRACE_AS_GLOBAL 0x2 -#define BIF_TRACE_AS_META 0x4 +#define BIF_TRACE_AS_LOCAL (0x1) +#define BIF_TRACE_AS_GLOBAL (0x2) +#define BIF_TRACE_AS_META (0x4) +#define BIF_TRACE_AS_CALL_TIME (0x8) extern erts_driver_t vanilla_driver; extern erts_driver_t spawn_driver; -- cgit v1.2.3 From 091c65e81be487d3cf69822d181996e6ebbfd502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 25 May 2010 18:16:25 +0200 Subject: Teach erlang:system_info/1 to list snifs erlang:system_info(snifs) lists all static native implemented functions. The function presents the lists with three tuple values containing MFAs [{Module, Function, Arity}, ...]. --- erts/emulator/beam/erl_bif_info.c | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 70d0e91a2c..48cda52612 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1892,6 +1892,37 @@ c_compiler_used(Eterm **hpp, Uint *szp) } +static int is_snif_term(Eterm module_atom) { + int i; + Atom *a = atom_tab(atom_val(module_atom)); + char *aname = (char *) a->name; + + /* if a->name has a '.' then the bif (snif) is bogus i.e a package */ + for (i = 0; i < a->len; i++) { + if (aname[i] == '.') + return 0; + } + + return 1; +} + +static Eterm build_snif_term(Eterm **hpp, Uint *szp, int ix, Eterm res) { + Eterm tup; + tup = erts_bld_tuple(hpp, szp, 3, bif_table[ix].module, bif_table[ix].name, make_small(bif_table[ix].arity)); + res = erts_bld_cons( hpp, szp, tup, res); + return res; +} + +static Eterm build_snifs_term(Eterm **hpp, Uint *szp, Eterm res) { + int i; + for (i = 0; i < BIF_SIZE; i++) { + if (is_snif_term(bif_table[i].module)) { + res = build_snif_term(hpp, szp, i, res); + } + } + return res; +} + BIF_RETTYPE system_info_1(BIF_ALIST_1) { Eterm res; @@ -1940,6 +1971,15 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(db_get_trace_control_word_0(BIF_P)); } else if (ERTS_IS_ATOM_STR("ets_realloc_moves", BIF_ARG_1)) { BIF_RET((erts_ets_realloc_always_moves) ? am_true : am_false); + } else if (ERTS_IS_ATOM_STR("snifs", BIF_ARG_1)) { + Uint size = 0; + Uint *szp; + + szp = &size; + build_snifs_term(NULL, szp, NIL); + hp = HAlloc(BIF_P, size); + res = build_snifs_term(&hp, NULL, NIL); + BIF_RET(res); } else if (BIF_ARG_1 == am_sequential_tracer) { val = erts_get_system_seq_tracer(); ASSERT(is_internal_pid(val) || is_internal_port(val) || val==am_false) -- cgit v1.2.3 From 1e1482a82280ffd16ca160845f8d7fd3d52a0aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 28 May 2010 20:43:56 +0200 Subject: Fix assertion in trace_pattern for bifs --- erts/emulator/beam/beam_bp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 9004c74f51..5ccac9b593 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -1042,7 +1042,8 @@ static int set_function_break(Module *modp, BeamInstr *pc, int bif, *pc == (BeamInstr) BeamOp(op_i_mtrace_breakpoint) || *pc == (BeamInstr) BeamOp(op_i_debug_breakpoint) || *pc == (BeamInstr) BeamOp(op_i_time_breakpoint) || - *pc == (BeamInstr) BeamOp(op_i_count_breakpoint)); + *pc == (BeamInstr) BeamOp(op_i_count_breakpoint) || + *pc == (BeamInstr) em_apply_bif); if (*pc == (BeamInstr) BeamOp(op_i_debug_breakpoint)) { /* Debug bp must be last, so if it is also first; * it must be singleton. */ -- cgit v1.2.3 From 9a4c67fa6c6093b11e387247e1ca756edefbdbfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 31 May 2010 20:03:56 +0200 Subject: Fix bp_hash_put to do hash value after rehash If a rehash was done the old hash value was used. This was incorrect. --- erts/emulator/beam/beam_bp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 5ccac9b593..682f31b83f 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -570,7 +570,7 @@ static ERTS_INLINE bp_data_time_item_t * bp_hash_get(bp_time_hash_t *hash, bp_da } static ERTS_INLINE bp_data_time_item_t * bp_hash_put(bp_time_hash_t *hash, bp_data_time_item_t* sitem) { - Uint hval = (sitem->pid >> 4) % hash->n; + Uint hval; float r = 0.0; bp_data_time_item_t *item; @@ -582,6 +582,8 @@ static ERTS_INLINE bp_data_time_item_t * bp_hash_put(bp_time_hash_t *hash, bp_da if (r > 0.7f) { bp_hash_rehash(hash, hash->n * 2); } + /* Do hval after rehash */ + hval = (sitem->pid >> 4) % hash->n; /* find free slot */ item = hash->item; -- cgit v1.2.3 From 63751e0554dfb35a63d572aaca2f1fc37e1c12fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Jun 2010 16:04:53 +0200 Subject: Fix ops.tab to include i_return_time_trace Without the instruction defined in ops the interpreter will not compile when using NO_JUMPTABLE. --- erts/emulator/beam/ops.tab | 1 + 1 file changed, 1 insertion(+) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 32abdccd7b..a2439d5582 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -85,6 +85,7 @@ i_mtrace_breakpoint i_debug_breakpoint i_count_breakpoint i_time_breakpoint +i_return_time_trace i_return_to_trace i_yield i_global_cons -- cgit v1.2.3