/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2000-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* %CopyrightEnd%
*/
#ifndef _BEAM_BP_H
#define _BEAM_BP_H
#include "sys.h"
#include "erl_vm.h"
#include "global.h"
/* 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; /* -"- */
BeamInstr orig_instr; /* The original instruction to execute */
BeamInstr this_instr; /* key */
} BpData;
/*
** All the following bp_data_.. structs must begin the same way
*/
typedef struct bp_data_trace {
struct bp_data *next;
struct bp_data *prev;
BeamInstr orig_instr;
BeamInstr this_instr; /* key */
Binary *match_spec;
Eterm tracer_pid;
} BpDataTrace;
typedef struct bp_data_debug {
struct bp_data *next;
struct bp_data *prev;
BeamInstr orig_instr;
BeamInstr this_instr; /* key */
} BpDataDebug;
typedef struct bp_data_count { /* Call count */
struct bp_data *next;
struct bp_data *prev;
BeamInstr orig_instr;
BeamInstr this_instr; /* key */
erts_smp_atomic_t acount;
} BpDataCount;
typedef struct {
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;
BeamInstr orig_instr;
BeamInstr this_instr; /* key */
Uint pause;
Uint n;
bp_time_hash_t *hash;
} BpDataTime;
typedef struct {
Uint ms;
Uint s;
Uint us;
BeamInstr *pc;
} process_breakpoint_time_t; /* used within psd */
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)
#define ErtsSmpBPUnlock(BDC) erts_smp_spin_unlock(&erts_bp_lock)
#else
#define ErtsSmpBPLock(BDC)
#define ErtsSmpBPUnlock(BDC)
#endif
#ifdef ERTS_SMP
#define bp_sched2ix_proc(p) ((p)->scheduler_data->no - 1)
#else
#define bp_sched2ix_proc(p) (0)
#endif
#define ErtsCountBreak(p, pc,instr_result) \
do { \
BpData **bds = (BpData **) (pc)[-4]; \
BpDataCount *bdc = NULL; \
Uint ix = bp_sched2ix_proc( (p) ); \
erts_aint_t count = 0; \
\
ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \
ASSERT(bds); \
bdc = (BpDataCount *) bds[ix]; \
bdc = (BpDataCount *) bdc->next; \
ASSERT(bdc); \
bds[ix] = (BpData *) bdc; \
count = erts_smp_atomic_read_nob(&bdc->acount); \
if (count >= 0) erts_smp_atomic_inc_nob(&bdc->acount); \
*(instr_result) = bdc->orig_instr; \
} while (0)
#define ErtsBreakSkip(p, pc,instr_result) \
do { \
BpData **bds = (BpData **) (pc)[-4]; \
BpData *bd = NULL; \
Uint ix = bp_sched2ix_proc( (p) ); \
\
ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \
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{
erts_break_nop = 0, /* Must be false */
erts_break_set = !0, /* Must be true */
erts_break_reset,
erts_break_stop
};
/*
** Function interface exported from beam_bp.c
*/
void erts_bp_init(void);
int erts_set_trace_break(Eterm mfa[3], int specified, Binary *match_spec,
Eterm tracer_pid);
int erts_clear_trace_break(Eterm mfa[3], int specified);
int erts_set_mtrace_break(Eterm mfa[3], int specified, Binary *match_spec,
Eterm tracer_pid);
int erts_clear_mtrace_break(Eterm mfa[3], int specified);
void erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec,
Eterm tracer_pid);
void erts_clear_mtrace_bif(BeamInstr *pc);
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_clear_break(Eterm mfa[3], int specified);
int erts_clear_module_break(Module *modp);
int erts_clear_function_break(Module *modp, BeamInstr *pc);
BeamInstr erts_trace_break(Process *p, BeamInstr *pc, Eterm *args,
Uint32 *ret_flags, Eterm *tracer_pid);
Uint32 erts_bif_mtrace(Process *p, BeamInstr *pc, Eterm *args,
int local, Eterm *tracer_pid);
int erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret,
Eterm *tracer_pid_ret);
int erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret,
Eterm *tracer_pid_rte);
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(Process *p, BeamInstr *pc, Eterm *call_time);
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);
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]);
ERTS_GLB_INLINE Uint erts_bp_sched2ix(void);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE Uint erts_bp_sched2ix(void)
{
#ifdef ERTS_SMP
ErtsSchedulerData *esdp;
esdp = erts_get_scheduler_data();
return esdp->no - 1;
#else
return 0;
#endif
}
#endif
#endif /* _BEAM_BP_H */