aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam/beam_bp.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/beam/beam_bp.c')
-rw-r--r--erts/emulator/beam/beam_bp.c785
1 files changed, 785 insertions, 0 deletions
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
new file mode 100644
index 0000000000..1abf1dc10c
--- /dev/null
+++ b/erts/emulator/beam/beam_bp.c
@@ -0,0 +1,785 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+#include "erl_process.h"
+#include "beam_load.h"
+#include "bif.h"
+#include "error.h"
+#include "erl_binary.h"
+#include "beam_bp.h"
+
+/* *************************************************************************
+** Macros
+*/
+
+/*
+** Memory allocation macros
+*/
+/* Breakpoint data */
+#define Alloc(SZ) erts_alloc(ERTS_ALC_T_BPD, (SZ))
+#define ReAlloc(P, SIZ) erts_realloc(ERTS_ALC_T_BPD, (P), (SZ))
+#define Free(P) erts_free(ERTS_ALC_T_BPD, (P))
+
+/*
+** Doubly linked ring macros
+*/
+
+#define BpInit(a,i) \
+do { \
+ (a)->orig_instr = (i); \
+ (a)->next = (a); \
+ (a)->prev = (a); \
+} while (0)
+
+#define BpSpliceNext(a,b) \
+do { \
+ register BpData *c = (a), *d = (b), *e; \
+ e = c->next->prev; \
+ c->next->prev = d->next->prev; \
+ d->next->prev = e; \
+ e = c->next; \
+ c->next = d->next; \
+ d->next = e; \
+} while (0)
+
+#define BpSplicePrev(a,b) \
+do { \
+ register BpData *c = (a), *d = (b), *e; \
+ e = c->prev->next; \
+ c->prev->next = d->prev->next; \
+ d->prev->next = e; \
+ e = c->prev; \
+ c->prev = d->prev; \
+ d->prev = e; \
+} while (0)
+
+#ifdef DEBUG
+# define BpSingleton(a) ((a)->next == (a) && (a)->prev == (a))
+#else
+# define BpSingleton(a) ((a)->next == (a))
+#endif
+
+#define BpInitAndSpliceNext(a,i,b) \
+do { \
+ (a)->orig_instr = (i); \
+ (a)->prev = (b); \
+ (b)->next->prev = (a); \
+ (a)->next = (b)->next; \
+ (b)->next = (a); \
+} while (0)
+
+#define BpInitAndSplicePrev(a,i,b) \
+do { \
+ (a)->orig_instr = (i); \
+ (a)->next = (b); \
+ (b)->prev->next = (a); \
+ (a)->prev = (b)->prev; \
+ (b)->prev = (a); \
+} while (0)
+
+/* *************************************************************************
+** Local prototypes
+*/
+
+/*
+** Helpers
+*/
+
+static int set_break(Eterm mfa[3], int specified,
+ Binary *match_spec, Uint break_op,
+ enum erts_break_op count_op, Eterm tracer_pid);
+static int set_module_break(Module *modp, Eterm mfa[3], int specified,
+ Binary *match_spec, Uint break_op,
+ enum erts_break_op count_op, Eterm tracer_pid);
+static int set_function_break(Module *modp, Uint *pc,
+ Binary *match_spec, Uint break_op,
+ enum erts_break_op count_op, Eterm tracer_pid);
+
+static int clear_break(Eterm mfa[3], int specified,
+ Uint break_op);
+static int clear_module_break(Module *modp, Eterm mfa[3], int specified,
+ Uint break_op);
+static int clear_function_break(Module *modp, Uint *pc,
+ Uint break_op);
+
+static BpData *is_break(Uint *pc, Uint break_op);
+
+
+
+/* *************************************************************************
+** External interfaces
+*/
+
+erts_smp_spinlock_t erts_bp_lock;
+
+void
+erts_bp_init(void) {
+ erts_smp_spinlock_init(&erts_bp_lock, "breakpoints");
+}
+
+int
+erts_set_trace_break(Eterm mfa[3], int specified, Binary *match_spec,
+ Eterm tracer_pid) {
+ ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0));
+ return set_break(mfa, specified, match_spec,
+ (Uint) BeamOp(op_i_trace_breakpoint), 0, tracer_pid);
+}
+
+int
+erts_set_mtrace_break(Eterm mfa[3], int specified, Binary *match_spec,
+ Eterm tracer_pid) {
+ ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0));
+ return set_break(mfa, specified, match_spec,
+ (Uint) BeamOp(op_i_mtrace_breakpoint), 0, tracer_pid);
+}
+
+void
+erts_set_mtrace_bif(Uint *pc, Binary *match_spec, Eterm tracer_pid) {
+ BpDataTrace *bdt;
+ ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0));
+
+ 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] = (Uint) bdt;
+ }
+}
+
+int
+erts_set_debug_break(Eterm mfa[3], int specified) {
+ ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0));
+ return set_break(mfa, specified, NULL,
+ (Uint) BeamOp(op_i_debug_breakpoint), 0, NIL);
+}
+
+int
+erts_set_count_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,
+ (Uint) BeamOp(op_i_count_breakpoint), count_op, NIL);
+}
+
+
+
+int
+erts_clear_trace_break(Eterm mfa[3], int specified) {
+ ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0));
+ return clear_break(mfa, specified,
+ (Uint) BeamOp(op_i_trace_breakpoint));
+}
+
+int
+erts_clear_mtrace_break(Eterm mfa[3], int specified) {
+ ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0));
+ return clear_break(mfa, specified,
+ (Uint) BeamOp(op_i_mtrace_breakpoint));
+}
+
+void
+erts_clear_mtrace_bif(Uint *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] = (Uint) NULL;
+}
+
+int
+erts_clear_debug_break(Eterm mfa[3], int specified) {
+ ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0));
+ return clear_break(mfa, specified,
+ (Uint) BeamOp(op_i_debug_breakpoint));
+}
+
+int
+erts_clear_count_break(Eterm mfa[3], int specified) {
+ ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0));
+ return clear_break(mfa, specified,
+ (Uint) BeamOp(op_i_count_breakpoint));
+}
+
+int
+erts_clear_break(Eterm mfa[3], int specified) {
+ ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0));
+ return clear_break(mfa, specified, 0);
+}
+
+int
+erts_clear_module_break(Module *modp) {
+ ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0));
+ ASSERT(modp);
+ return clear_module_break(modp, NULL, 0, 0);
+}
+
+int
+erts_clear_function_break(Module *modp, Uint *pc) {
+ ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0));
+ ASSERT(modp);
+ return clear_function_break(modp, pc, 0);
+}
+
+
+
+/*
+ * SMP NOTE: Process p may have become exiting on return!
+ */
+Uint
+erts_trace_break(Process *p, Uint *pc, Eterm *args,
+ Uint32 *ret_flags, Eterm *tracer_pid) {
+ Eterm tpid1, tpid2;
+ BpDataTrace *bdt = (BpDataTrace *) pc[-4];
+
+ ASSERT(pc[-5] == (Uint) BeamOp(op_i_func_info_IaaI));
+ ASSERT(bdt);
+ bdt = (BpDataTrace *) bdt->next;
+ ASSERT(bdt);
+ ASSERT(ret_flags);
+ ASSERT(tracer_pid);
+
+ ErtsSmpBPLock(bdt);
+ tpid1 = tpid2 = bdt->tracer_pid;
+ ErtsSmpBPUnlock(bdt);
+
+ *ret_flags = erts_call_trace(p, pc-3/*mfa*/, bdt->match_spec, args,
+ 1, &tpid2);
+ *tracer_pid = tpid2;
+ if (tpid1 != tpid2) {
+ ErtsSmpBPLock(bdt);
+ bdt->tracer_pid = tpid2;
+ ErtsSmpBPUnlock(bdt);
+ }
+ pc[-4] = (Uint) bdt;
+ return bdt->orig_instr;
+}
+
+
+
+/*
+ * SMP NOTE: Process p may have become exiting on return!
+ */
+Uint32
+erts_bif_mtrace(Process *p, Uint *pc, Eterm *args, int local,
+ Eterm *tracer_pid) {
+ BpDataTrace *bdt = (BpDataTrace *) pc[-4];
+
+ ASSERT(tracer_pid);
+ if (bdt) {
+ Eterm tpid1, tpid2;
+ Uint32 flags;
+
+ ErtsSmpBPLock(bdt);
+ tpid1 = tpid2 = bdt->tracer_pid;
+ ErtsSmpBPUnlock(bdt);
+
+ flags = erts_call_trace(p, pc-3/*mfa*/, bdt->match_spec, args,
+ local, &tpid2);
+ *tracer_pid = tpid2;
+ if (tpid1 != tpid2) {
+ ErtsSmpBPLock(bdt);
+ bdt->tracer_pid = tpid2;
+ ErtsSmpBPUnlock(bdt);
+ }
+ return flags;
+ }
+ *tracer_pid = NIL;
+ return 0;
+}
+
+
+
+int
+erts_is_trace_break(Uint *pc, Binary **match_spec_ret, Eterm *tracer_pid_ret) {
+ BpDataTrace *bdt =
+ (BpDataTrace *) is_break(pc, (Uint) BeamOp(op_i_trace_breakpoint));
+
+ 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_mtrace_break(Uint *pc, Binary **match_spec_ret, Eterm *tracer_pid_ret) {
+ BpDataTrace *bdt =
+ (BpDataTrace *) is_break(pc, (Uint) BeamOp(op_i_mtrace_breakpoint));
+
+ 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_mtrace_bif(Uint *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(Uint *pc) {
+#ifdef HIPE
+ ASSERT(pc[-5] == (Uint) BeamOp(op_i_func_info_IaaI));
+ return pc[0] == (Uint) BeamOp(op_hipe_trap_call)
+ || pc[0] == (Uint) BeamOp(op_hipe_trap_call_closure);
+#else
+ return 0;
+#endif
+}
+
+int
+erts_is_count_break(Uint *pc, Sint *count_ret) {
+ BpDataCount *bdc =
+ (BpDataCount *) is_break(pc, (Uint) BeamOp(op_i_count_breakpoint));
+
+ if (bdc) {
+ if (count_ret) {
+ ErtsSmpBPLock(bdc);
+ *count_ret = bdc->count;
+ ErtsSmpBPUnlock(bdc);
+ }
+ return !0;
+ }
+ return 0;
+}
+
+Uint *
+erts_find_local_func(Eterm mfa[3]) {
+ Module *modp;
+ Uint** code_base;
+ Uint* code_ptr;
+ Uint i,n;
+
+ if ((modp = erts_get_module(mfa[0])) == NULL)
+ return NULL;
+ if ((code_base = (Uint **) modp->code) == NULL)
+ return NULL;
+ n = (Uint) code_base[MI_NUM_FUNCTIONS];
+ for (i = 0; i < n; ++i) {
+ code_ptr = code_base[MI_FUNCTIONS+i];
+ ASSERT(((Uint) BeamOp(op_i_func_info_IaaI)) == code_ptr[0]);
+ ASSERT(mfa[0] == ((Eterm) code_ptr[2]));
+ if (mfa[1] == ((Eterm) code_ptr[3]) &&
+ ((Uint) mfa[2]) == code_ptr[4]) {
+ return code_ptr + 5;
+ }
+ }
+ return NULL;
+}
+
+
+
+/* *************************************************************************
+** Local helpers
+*/
+
+
+static int set_break(Eterm mfa[3], int specified,
+ Binary *match_spec, Eterm break_op,
+ enum erts_break_op count_op, Eterm tracer_pid)
+{
+ Module *modp;
+ int num_processed = 0;
+ if (!specified) {
+ /* Find and process all modules in the system... */
+ int current;
+ int last = module_code_size();
+ for (current = 0; current < last; current++) {
+ modp = module_code(current);
+ ASSERT(modp != NULL);
+ num_processed +=
+ set_module_break(modp, mfa, specified,
+ match_spec, break_op, count_op,
+ tracer_pid);
+ }
+ } else {
+ /* Process a single module */
+ if ((modp = erts_get_module(mfa[0])) != NULL) {
+ num_processed +=
+ set_module_break(modp, mfa, specified,
+ match_spec, break_op, count_op,
+ tracer_pid);
+ }
+ }
+ return num_processed;
+}
+
+static int set_module_break(Module *modp, Eterm mfa[3], int specified,
+ Binary *match_spec, Uint break_op,
+ enum erts_break_op count_op, Eterm tracer_pid) {
+ Uint** code_base;
+ Uint* code_ptr;
+ int num_processed = 0;
+ Uint i,n;
+
+ ASSERT(break_op);
+ ASSERT(modp);
+ code_base = (Uint **) modp->code;
+ if (code_base == NULL) {
+ return 0;
+ }
+ n = (Uint) code_base[MI_NUM_FUNCTIONS];
+ for (i = 0; i < n; ++i) {
+ code_ptr = code_base[MI_FUNCTIONS+i];
+ ASSERT(code_ptr[0] == (Uint) BeamOp(op_i_func_info_IaaI));
+ if ((specified < 2 || mfa[1] == ((Eterm) code_ptr[3])) &&
+ (specified < 3 || ((int) mfa[2]) == ((int) code_ptr[4]))) {
+ Uint *pc = code_ptr+5;
+
+ num_processed +=
+ set_function_break(modp, pc, match_spec,
+ break_op, count_op, tracer_pid);
+ }
+ }
+ return num_processed;
+}
+
+static int set_function_break(Module *modp, Uint *pc,
+ Binary *match_spec, Uint break_op,
+ enum erts_break_op count_op, Eterm tracer_pid) {
+ BpData *bd, **r;
+ size_t size;
+ Uint **code_base = (Uint **)modp->code;
+
+ ASSERT(code_base);
+ ASSERT(code_base <= (Uint **)pc);
+ ASSERT((Uint **)pc < code_base + (modp->code_length/sizeof(Uint *)));
+ /*
+ * Currently no trace support for native code.
+ */
+ if (erts_is_native_break(pc)) {
+ return 0;
+ }
+ /* Do not allow two breakpoints of the same kind */
+ if ( (bd = is_break(pc, break_op))) {
+ if (break_op == (Uint) BeamOp(op_i_trace_breakpoint)
+ || break_op == (Uint) BeamOp(op_i_mtrace_breakpoint)) {
+ BpDataTrace *bdt = (BpDataTrace *) bd;
+ Binary *old_match_spec;
+
+ /* Update match spec and tracer */
+ MatchSetRef(match_spec);
+ ErtsSmpBPLock(bdt);
+ old_match_spec = bdt->match_spec;
+ bdt->match_spec = match_spec;
+ bdt->tracer_pid = tracer_pid;
+ ErtsSmpBPUnlock(bdt);
+ MatchSetUnref(old_match_spec);
+ } else {
+ ASSERT(! match_spec);
+ ASSERT(is_nil(tracer_pid));
+ if (break_op == (Uint) BeamOp(op_i_count_breakpoint)) {
+ BpDataCount *bdc = (BpDataCount *) bd;
+
+ ErtsSmpBPLock(bdc);
+ if (count_op == erts_break_stop) {
+ if (bdc->count >= 0) {
+ bdc->count = -bdc->count-1; /* Stop call counter */
+ }
+ } else {
+ bdc->count = 0; /* Reset call counter */
+ }
+ ErtsSmpBPUnlock(bdc);
+ } else {
+ ASSERT (! count_op);
+ }
+ }
+ return 1;
+ }
+ if (break_op == (Uint) BeamOp(op_i_trace_breakpoint) ||
+ break_op == (Uint) BeamOp(op_i_mtrace_breakpoint)) {
+ size = sizeof(BpDataTrace);
+ } else {
+ ASSERT(! match_spec);
+ ASSERT(is_nil(tracer_pid));
+ if (break_op == (Uint) BeamOp(op_i_count_breakpoint)) {
+ if (count_op == erts_break_reset
+ || count_op == erts_break_stop) {
+ /* Do not insert a new breakpoint */
+ return 1;
+ }
+ size = sizeof(BpDataCount);
+ } else {
+ ASSERT(! count_op);
+ ASSERT(break_op == (Uint) BeamOp(op_i_debug_breakpoint));
+ size = sizeof(BpDataDebug);
+ }
+ }
+ r = (BpData **) (pc-4);
+ if (! *r) {
+ ASSERT(*pc != (Uint) BeamOp(op_i_trace_breakpoint));
+ ASSERT(*pc != (Uint) BeamOp(op_i_mtrace_breakpoint));
+ ASSERT(*pc != (Uint) BeamOp(op_i_debug_breakpoint));
+ ASSERT(*pc != (Uint) BeamOp(op_i_count_breakpoint));
+ /* First breakpoint; create singleton ring */
+ bd = Alloc(size);
+ BpInit(bd, *pc);
+ *pc = break_op;
+ *r = bd;
+ } else {
+ ASSERT(*pc == (Uint) BeamOp(op_i_trace_breakpoint) ||
+ *pc == (Uint) BeamOp(op_i_mtrace_breakpoint) ||
+ *pc == (Uint) BeamOp(op_i_debug_breakpoint) ||
+ *pc == (Uint) BeamOp(op_i_count_breakpoint));
+ if (*pc == (Uint) BeamOp(op_i_debug_breakpoint)) {
+ /* Debug bp must be last, so if it is also first;
+ * it must be singleton. */
+ 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
+ == (Uint) BeamOp(op_i_debug_breakpoint)) {
+ /* Debug bp last in the ring; insert new second to last. */
+ bd = Alloc(size);
+ BpInitAndSplicePrev(bd, (*r)->prev->orig_instr, *r);
+ (*r)->prev->orig_instr = break_op;
+ } else {
+ /* Just insert last in the ring */
+ bd = Alloc(size);
+ BpInitAndSpliceNext(bd, (*r)->orig_instr, *r);
+ (*r)->orig_instr = break_op;
+ *r = bd;
+ }
+ }
+ /* Init the bp type specific data */
+ if (break_op == (Uint) BeamOp(op_i_trace_breakpoint) ||
+ break_op == (Uint) BeamOp(op_i_mtrace_breakpoint)) {
+
+ BpDataTrace *bdt = (BpDataTrace *) bd;
+
+ MatchSetRef(match_spec);
+ bdt->match_spec = match_spec;
+ bdt->tracer_pid = tracer_pid;
+ } else if (break_op == (Uint) BeamOp(op_i_count_breakpoint)) {
+ BpDataCount *bdc = (BpDataCount *) bd;
+
+ bdc->count = 0;
+ }
+ ++(*(Uint*)&code_base[MI_NUM_BREAKPOINTS]);
+ return 1;
+}
+
+static int clear_break(Eterm mfa[3], int specified, Uint break_op)
+{
+ int num_processed = 0;
+ Module *modp;
+
+ if (!specified) {
+ /* Iterate over all modules */
+ int current;
+ int last = module_code_size();
+
+ for (current = 0; current < last; current++) {
+ modp = module_code(current);
+ ASSERT(modp != NULL);
+ num_processed += clear_module_break(modp, mfa, specified, break_op);
+ }
+ } else {
+ /* Process a single module */
+ if ((modp = erts_get_module(mfa[0])) != NULL) {
+ num_processed +=
+ clear_module_break(modp, mfa, specified, break_op);
+ }
+ }
+ return num_processed;
+}
+
+static int clear_module_break(Module *m, Eterm mfa[3], int specified,
+ Uint break_op) {
+ Uint** code_base;
+ Uint* code_ptr;
+ int num_processed = 0;
+ Uint i,n;
+
+ ASSERT(m);
+ code_base = (Uint **) m->code;
+ if (code_base == NULL) {
+ return 0;
+ }
+ n = (Uint) code_base[MI_NUM_FUNCTIONS];
+ for (i = 0; i < n; ++i) {
+ code_ptr = code_base[MI_FUNCTIONS+i];
+ if ((specified < 2 || mfa[1] == ((Eterm) code_ptr[3])) &&
+ (specified < 3 || ((int) mfa[2]) == ((int) code_ptr[4]))) {
+ Uint *pc = code_ptr + 5;
+
+ num_processed +=
+ clear_function_break(m, pc, break_op);
+ }
+ }
+ return num_processed;
+}
+
+static int clear_function_break(Module *m, Uint *pc, Uint break_op) {
+ BpData *bd;
+ Uint **code_base = (Uint **)m->code;
+
+ ASSERT(code_base);
+ ASSERT(code_base <= (Uint **)pc);
+ ASSERT((Uint **)pc < code_base + (m->code_length/sizeof(Uint *)));
+ /*
+ * 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,
+ * but break_op may be 0 which matches any type.
+ */
+ Uint op;
+ BpData **r = (BpData **) (pc-4);
+
+ ASSERT(*r);
+ /* Find opcode for this breakpoint */
+ if (break_op) {
+ op = break_op;
+ } else {
+ if (bd == (*r)->next) {
+ /* First breakpoint in ring */
+ op = *pc;
+ } else {
+ op = bd->prev->orig_instr;
+ }
+ }
+ if (BpSingleton(bd)) {
+ ASSERT(*r == bd);
+ /* Only one breakpoint to remove */
+ *r = NULL;
+ *pc = bd->orig_instr;
+ } else {
+ BpData *bd_prev = bd->prev;
+
+ BpSpliceNext(bd, bd_prev);
+ ASSERT(BpSingleton(bd));
+ if (bd == *r) {
+ /* We removed the last breakpoint in the ring */
+ *r = bd_prev;
+ bd_prev->orig_instr = bd->orig_instr;
+ } else if (bd_prev == *r) {
+ /* We removed the first breakpoint in the ring */
+ *pc = bd->orig_instr;
+ } else {
+ bd_prev->orig_instr = bd->orig_instr;
+ }
+ }
+ if (op == (Uint) BeamOp(op_i_trace_breakpoint) ||
+ op == (Uint) BeamOp(op_i_mtrace_breakpoint)) {
+
+ BpDataTrace *bdt = (BpDataTrace *) bd;
+
+ MatchSetUnref(bdt->match_spec);
+ }
+ Free(bd);
+ ASSERT(((Uint) code_base[MI_NUM_BREAKPOINTS]) > 0);
+ --(*(Uint*)&code_base[MI_NUM_BREAKPOINTS]);
+ }
+ return 1;
+}
+
+
+
+/*
+** Searches (linear forward) the breakpoint ring for a specified opcode
+** and returns a pointer to the breakpoint data structure or NULL if
+** not found. If the specified opcode is 0, the last breakpoint is
+** returned. The program counter must point to the first executable
+** (breakpoint) instruction of the function.
+*/
+static BpData *is_break(Uint *pc, Uint break_op) {
+ ASSERT(pc[-5] == (Uint) BeamOp(op_i_func_info_IaaI));
+ if (! erts_is_native_break(pc)) {
+ BpData *bd = (BpData *) pc[-4];
+
+ if (break_op == 0) {
+ return bd;
+ }
+ if (*pc == break_op) {
+ ASSERT(bd);
+ return bd->next;
+ }
+ if (! bd){
+ return NULL;
+ }
+ bd = bd->next;
+ while (bd != (BpData *) pc[-4]) {
+ ASSERT(bd);
+ if (bd->orig_instr == break_op) {
+ bd = bd->next;
+ ASSERT(bd);
+ return bd;
+ } else {
+ bd = bd->next;
+ }
+ }
+ }
+ return NULL;
+}