Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental debugger API #9604

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion erts/emulator/Makefile.in
Original file line number Diff line number Diff line change
@@ -1117,7 +1117,7 @@ RUN_OBJS += \
$(OBJDIR)/erl_trace.o $(OBJDIR)/copy.o \
$(OBJDIR)/utils.o $(OBJDIR)/bif.o \
$(OBJDIR)/io.o $(OBJDIR)/erl_printf_term.o\
$(OBJDIR)/erl_debug.o \
$(OBJDIR)/erl_debug.o $(OBJDIR)/erl_debugger.o \
$(OBJDIR)/erl_message.o $(OBJDIR)/erl_proc_sig_queue.o \
$(OBJDIR)/erl_process_dict.o $(OBJDIR)/erl_process_lock.o \
$(OBJDIR)/erl_port_task.o $(OBJDIR)/erl_arith.o \
4 changes: 4 additions & 0 deletions erts/emulator/beam/atom.names
Original file line number Diff line number Diff line change
@@ -215,6 +215,7 @@ atom current_location
atom current_stacktrace
atom data
atom debug_flags
atom debugger_event
atom decentralized_counters
atom decimals
atom default
@@ -406,6 +407,7 @@ atom Le='=<'
atom legacy
atom lf
atom line
atom line_breakpoint
atom line_counters
atom line_delimiter
atom line_length
@@ -688,6 +690,7 @@ atom siginfo
atom silent
atom size
atom skip
atom slots
atom spawn_executable
atom spawn_driver
atom spawn_init
@@ -764,6 +767,7 @@ atom unloaded
atom unloaded_only
atom unload_cancelled
atom unsafe
atom unsupported
atom value
atom version
atom visible
127 changes: 127 additions & 0 deletions erts/emulator/beam/beam_bp.c
Original file line number Diff line number Diff line change
@@ -1492,6 +1492,133 @@ int erts_is_call_break(Process *p, ErtsTraceSession *session, int is_time,
return 1;
}

void erts_install_line_breakpoint(struct erl_module_instance *mi, ErtsCodePtr cp_exec) {
ErtsCodePtr cp_rw;

erts_unseal_module(mi);
cp_rw = erts_writable_code_ptr(mi, cp_exec);

#ifdef BEAMASM
erts_asm_bp_enable(cp_rw);
#else
{
BeamInstr volatile *pc = (BeamInstr*)cp_rw;
BeamInstr instr = *pc;
BeamInstr br = BeamOpCodeAddr(op_i_enabled_line_breakpoint_t);

/* The following write is not protected by any lock.
* See note in erts_install_breakpoints().
*/
instr = BeamSetCodeAddr(instr, br);
*pc = instr;
}
#endif

erts_seal_module(mi);
}

void erts_uninstall_line_breakpoint(struct erl_module_instance *mi, ErtsCodePtr cp_exec) {
ErtsCodePtr cp_rw;

erts_unseal_module(mi);
cp_rw = erts_writable_code_ptr(mi, cp_exec);

#ifdef BEAMASM
erts_asm_bp_disable(cp_rw);
#else
{
BeamInstr volatile *pc = (BeamInstr*)cp_rw;
BeamInstr instr = *pc;
BeamInstr br = BeamOpCodeAddr(op_i_disabled_line_breakpoint_t);

/* The following write is not protected by any lock.
* See note in erts_install_breakpoints().
*/
instr = BeamSetCodeAddr(instr, br);
*pc = instr;
}
#endif

erts_seal_module(mi);
}

enum erts_is_line_breakpoint erts_is_line_breakpoint_code(ErtsCodePtr p) {
#ifdef BEAMASM
return beamasm_is_line_breakpoint_trampoline(p);
#else
const UWord instr = *(UWord *)p;
if (BeamIsOpCode(instr, op_i_disabled_line_breakpoint_t))
return IS_DISABLED_LINE_BP;
if (BeamIsOpCode(instr, op_i_enabled_line_breakpoint_t))
return IS_ENABLED_LINE_BP;
return IS_NOT_LINE_BP;
#endif
}

const Export *
erts_line_breakpoint_hit__prepare_call(Process* c_p, ErtsCodePtr pc, Uint live, Eterm *regs, UWord *stk) {
FunctionInfo fi;
const Export *ep;

ASSERT(live <= MAX_REG);

/*
* Search the error_handler module
*/
ep = erts_find_function(am_erts_internal, am_breakpoint, 4,
erts_active_code_ix());
if (ep == NULL) {
/* No error handler */
return NULL;
}

/*
* Find breakpoint location
*/
erts_lookup_function_info(&fi, pc, 1);
if (!fi.mfa) {
return NULL;
}

if (ep->info.mfa.module == fi.mfa->module
&& ep->info.mfa.function == fi.mfa->function
&& ep->info.mfa.arity == fi.mfa->arity) {
/* Cycle breaker */
return NULL;
}

/*
* Save live regs on the stack
*/
for(int i = 0; i < live; i++) {
*(stk++) = regs[i];
}

regs[0] = fi.mfa->module;
regs[1] = fi.mfa->function;
regs[2] = make_small(fi.mfa->arity);
regs[3] = make_small(LOC_LINE(fi.loc));

return ep;
}

Uint
erts_line_breakpoint_hit__cleanup(Eterm *regs, UWord *stk) {
int i = 0;

/*
* Restore X-registers
*/
while(is_not_CP(*stk)) {
regs[i++] = *(stk++);
}

/*
* Return number of registers restored
*/
return i;
}

const ErtsCodeInfo *
erts_find_local_func(const ErtsCodeMFA *mfa) {
const BeamCodeHeader *code_hdr;
17 changes: 17 additions & 0 deletions erts/emulator/beam/beam_bp.h
Original file line number Diff line number Diff line change
@@ -116,6 +116,12 @@ typedef struct {
BpFunction* matching; /* Matching functions */
} BpFunctions;

enum erts_is_line_breakpoint {
IS_NOT_LINE_BP = 0,
IS_ENABLED_LINE_BP = 1,
IS_DISABLED_LINE_BP = 2,
};

/*
** Function interface exported from beam_bp.c
*/
@@ -187,6 +193,17 @@ void erts_clear_memory_break(BpFunctions *f);
Eterm erts_make_bp_session_list(ErtsHeapFactory*, const ErtsCodeInfo*,
Eterm tail);

void erts_install_line_breakpoint(struct erl_module_instance *, ErtsCodePtr);
void erts_uninstall_line_breakpoint(struct erl_module_instance *, ErtsCodePtr);
enum erts_is_line_breakpoint erts_is_line_breakpoint_code(ErtsCodePtr);

const Export *erts_line_breakpoint_hit__prepare_call(Process* c_p,
ErtsCodePtr pc,
Uint live,
Eterm *regs,
UWord *stk);
Uint erts_line_breakpoint_hit__cleanup(Eterm *regs, UWord *stk);

const ErtsCodeInfo *erts_find_local_func(const ErtsCodeMFA *mfa);

#if ERTS_GLB_INLINE_INCL_FUNC_DEF
5 changes: 5 additions & 0 deletions erts/emulator/beam/beam_code.h
Original file line number Diff line number Diff line change
@@ -107,6 +107,11 @@ typedef struct beam_code_header {
const BeamDebugTab *debug;
#endif

/*
* Debugging instrumentation to use while loading the module
*/
Uint debugger_flags;

/*
* Pointer to the module MD5 sum (16 bytes)
*/
1 change: 1 addition & 0 deletions erts/emulator/beam/beam_common.h
Original file line number Diff line number Diff line change
@@ -285,6 +285,7 @@ extern ErtsCodePtr beam_bif_export_trap;
extern ErtsCodePtr beam_export_trampoline;
extern ErtsCodePtr beam_continue_exit;
extern ErtsCodePtr beam_unloaded_fun;
extern ErtsCodePtr beam_i_line_breakpoint_cleanup;

extern ErtsCodePtr beam_return_to_trace; /* OpCode(i_return_to_trace) */
extern ErtsCodePtr beam_return_trace; /* OpCode(i_return_trace) */
50 changes: 50 additions & 0 deletions erts/emulator/beam/beam_ranges.c
Original file line number Diff line number Diff line change
@@ -364,3 +364,53 @@ lookup_loc(FunctionInfo* fi, const void* pc,
}
}
}

ErtsCodePtr
erts_find_next_code_for_line(const BeamCodeHeader* code_hdr,
unsigned int line,
unsigned int *start_from)
{
const BeamCodeLineTab *lt = code_hdr->line_table;
const UWord num_functions = code_hdr->num_functions;
unsigned int line_index = -1;
unsigned int num_lines;

if (lt == NULL) {
return NULL;
}

num_lines = lt->func_tab[num_functions] - lt->func_tab[0];

/* NB. While at the moment lt->loc_tab is sorted (except at
* the edges, since module_info/0,1, etc have no line info),
* there's no strong guarantee this will be sorted in general,
* as the compiler could reorder functions, code with no
* dependencies, etc. So we do a linear-search here.
*/
if (lt->loc_size == 2) {
for(unsigned int i=*start_from; i<num_lines; i++) {
int curr_line = LOC_LINE(lt->loc_tab.p2[i]);
if (curr_line == line) {
line_index = i;
*start_from = i+1;
break;
}
}
} else {
for(unsigned int i=*start_from; i<num_lines; i++) {
int curr_line = LOC_LINE(lt->loc_tab.p4[i]);
if (curr_line == line) {
line_index = i;
*start_from = i+1;
break;
}
}
}

if (line_index == -1) {
*start_from = 0;
return NULL;
}

return lt->func_tab[0][line_index];
}
17 changes: 15 additions & 2 deletions erts/emulator/beam/bif.tab
Original file line number Diff line number Diff line change
@@ -416,7 +416,7 @@ bif erl_ddll:monitor/2
bif erl_ddll:demonitor/1

#
# Bifs in the re module
# Bifs in the re module
#
bif re:version/0
bif re:compile/1
@@ -597,7 +597,7 @@ bif erlang:dt_get_tag_data/0
bif erlang:dt_spread_tag/1
bif erlang:dt_restore_tag/1

# These are dummies even with enabled dynamic trace unless vm probes are enabled.
# These are dummies even with enabled dynamic trace unless vm probes are enabled.
# They are also internal, for dtrace tags sent to the VM's own drivers (efile)
bif erlang:dt_prepend_vm_tag_data/1
bif erlang:dt_append_vm_tag_data/1
@@ -811,3 +811,16 @@ bif erts_internal:processes_next/1
bif code:get_debug_info/1
bif erlang:exit/3
bif erlang:link/2

bif erl_debugger:supported/0
bif erl_debugger:instrumentations/0
bif erl_debugger:toggle_instrumentations/1
bif erl_debugger:register/1
bif erl_debugger:unregister/2
bif erl_debugger:whereis/0
bif erl_debugger:breakpoint/3
bif erts_internal:notify_breakpoint_hit/3
bif erl_debugger:stack_frames/2
bif erl_debugger:peek_stack_frame_slot/4
bif erl_debugger:xregs_count/1
bif erl_debugger:peek_xreg/3
6 changes: 6 additions & 0 deletions erts/emulator/beam/emu/beam_emu.c
Original file line number Diff line number Diff line change
@@ -119,6 +119,8 @@ ErtsCodePtr beam_exit;
static BeamInstr beam_continue_exit_[1];
ErtsCodePtr beam_continue_exit;

static BeamInstr beam_i_line_breakpoint_cleanup_[1];
ErtsCodePtr beam_i_line_breakpoint_cleanup;

/* NOTE These should be the only variables containing trace instructions.
** Sometimes tests are for the instruction value, and sometimes
@@ -580,6 +582,7 @@ void process_main(ErtsSchedulerData *esdp)
OpCase(label_L):
OpCase(on_load):
OpCase(line_I):
OpCase(i_debug_line_It):
OpCase(i_nif_padding):
erts_exit(ERTS_ERROR_EXIT, "meta op\n");

@@ -688,6 +691,9 @@ init_emulator_finish(void)
beam_continue_exit_[0] = BeamOpCodeAddr(op_continue_exit);
beam_continue_exit = (ErtsCodePtr)&beam_continue_exit_[0];

beam_i_line_breakpoint_cleanup_[0] = BeamOpCodeAddr(op_i_line_breakpoint_cleanup);
beam_i_line_breakpoint_cleanup = (ErtsCodePtr)&beam_i_line_breakpoint_cleanup_[0];

beam_return_to_trace_[0] = BeamOpCodeAddr(op_i_return_to_trace);
beam_return_to_trace = (ErtsCodePtr)&beam_return_to_trace_[0];

Loading

Unchanged files with check annotations Beta

NoOnLoadMs = lists:droplast(Ms),
{error,[{HangingOnLoad,pending_on_load}]} =
code:atomic_load(NoOnLoadMs),
hanging_on_load ! stop_hanging_and_unload,

Check warning on line 183 in lib/kernel/test/multi_load_SUITE.erl

GitHub Actions / CT Test Results

on_load_failing failed

artifacts/Unit Test Results/kernel_junit.xml [took 0s]
Raw output
Test on_load_failing in multi_load_SUITE failed!
{badarg,[{erlang,send,
                 [hanging_on_load,stop_hanging_and_unload],
                 [{error_info,#{module => erl_erts_errors}}]},
         {multi_load_SUITE,on_load_failing,1,
                           [{file,"multi_load_SUITE.erl"},{line,183}]},
         {test_server,ts_tc,3,[{file,"test_server.erl"},{line,1794}]},
         {test_server,run_test_case_eval1,6,
                      [{file,"test_server.erl"},{line,1303}]},
         {test_server,run_test_case_eval,9,
                      [{file,"test_server.erl"},{line,1235}]}]}
ok.
%% Test cases starts here.
%%
%% Tests the applications consistency.
app_test(Config) when is_list(Config) ->

Check warning on line 86 in lib/kernel/test/kernel_SUITE.erl

GitHub Actions / CT Test Results

app_test failed

artifacts/Unit Test Results/kernel_junit.xml [took 0s]
Raw output
Test app_test in kernel_SUITE failed!
{test_case_failed,4}
ok=test_server:app_test(kernel),
ok.