# HG changeset patch # User Petter T. # Date 1691395849 -7200 # Node ID 02faaad763d4ed098e1bf4a72efd00a8a57d474e # Parent 56acd8f390f83c9dda28930c3280a0d1319b7474 VM Add support for echo Introcudes flag in tree_evaluator that can be checked to see if echo, debug, VM profiler is enabled. * libinterp/corefcn/compile.cc: Notify tree_evalutor VM profiler is active * libinterp/parse-tree/pt-bytecode-vm.cc: Support echo, check new flag dbgprofecho * libinterp/parse-tree/pt-bytecode-vm.h: New flag * libinterp/parse-tree/pt-bytecode-walk.cc: 'Is script' flag for unwind data * libinterp/parse-tree/pt-bytecode.h: * libinterp/parse-tree/pt-eval.cc: Set dbgprofecho flag. Expose echo state to VM. * libinterp/parse-tree/pt-eval.h: diff -r 56acd8f390f8 -r 02faaad763d4 libinterp/corefcn/compile.cc --- a/libinterp/corefcn/compile.cc Sat Aug 19 13:05:33 2023 +0200 +++ b/libinterp/corefcn/compile.cc Mon Aug 07 10:10:49 2023 +0200 @@ -189,12 +189,7 @@ { int nargin = args.length (); - // Unless a "profiler enabled" flag is added to the evaluator - // the vm profiler need the debugger to be active for it to actually - // be able to profile. - if (!interp.get_evaluator ().debug_mode ()) - warning ("As a workaround atleast one breakpoint has to be set" - " in any file (preferably not being profiled) for the profiler to actually profile anything."); + auto &evaler = interp.get_evaluator (); std::string arg0; @@ -208,9 +203,11 @@ vm::m_vm_profiler = std::make_shared (); vm::m_profiler_enabled = true; + evaler.vm_set_profiler_active (true); } else { + evaler.vm_set_profiler_active (false); vm::m_profiler_enabled = false; auto p = vm::m_vm_profiler; vm::m_vm_profiler = nullptr; @@ -224,6 +221,7 @@ vm::m_profiler_enabled = false; vm::m_vm_profiler = std::make_shared (); vm::m_profiler_enabled = true; + evaler.vm_set_profiler_active (true); } else if (arg0 == "resume") { @@ -231,13 +229,16 @@ vm::m_vm_profiler = std::make_shared (); vm::m_profiler_enabled = true; + evaler.vm_set_profiler_active (true); } else if (arg0 == "off") { + evaler.vm_set_profiler_active (false); vm::m_profiler_enabled = false; } else if (arg0 == "clear") { + evaler.vm_set_profiler_active (false); vm::m_profiler_enabled = false; vm::m_vm_profiler = nullptr; } diff -r 56acd8f390f8 -r 02faaad763d4 libinterp/parse-tree/pt-bytecode-vm.cc --- a/libinterp/parse-tree/pt-bytecode-vm.cc Sat Aug 19 13:05:33 2023 +0200 +++ b/libinterp/parse-tree/pt-bytecode-vm.cc Mon Aug 07 10:10:49 2023 +0200 @@ -642,7 +642,7 @@ /* PRINT_VM_STATE ("%d" COMMA __LINE__); */ \ /* CHECK_STACK (0); */ \ \ - if (OCTAVE_UNLIKELY (m_tw->debug_mode ())) /* Do we need to check for breakpoints? */\ + if (OCTAVE_UNLIKELY (m_tw->vm_dbgprofecho_flag ())) /* Do we need to check for breakpoints? */\ goto debug_check;\ int opcode = ip[0];\ arg0 = ip[1];\ @@ -659,7 +659,7 @@ /* PRINT_VM_STATE ("%d" COMMA __LINE__); */ \ /* CHECK_STACK (0); */ \ \ - if (OCTAVE_UNLIKELY (m_tw->debug_mode ())) /* Do we need to check for breakpoints? */\ + if (OCTAVE_UNLIKELY (m_tw->vm_dbgprofecho_flag ())) /* Do we need to check for breakpoints? */\ goto debug_check_1b;\ int opcode = arg0;\ arg0 = *ip++;\ @@ -4344,6 +4344,8 @@ m_ip = ip - code; m_unwind_data = unwind_data; + m_echo_prior_op_was_cond = false; // Used by the echo functionality + // Ther error_type is put on the stack before the jump to unwind. error_type et = static_cast (m_sp[-1].i); m_sp--; @@ -5674,6 +5676,7 @@ PRINT_VM_STATE ("Trace: "); } + // Handle the VM profiler if (OCTAVE_UNLIKELY (m_profiler_enabled)) { int64_t t1 = vm_profiler::unow (); @@ -5702,43 +5705,97 @@ } } + // Handle the echo functionality. + if (m_tw->echo ()) + { + int ip_offset = ip - code; + // In the beginning of functions we need to push an echo state for the function. + // push_echo_state () checks e.g. if the current function is supposed to be printed. + // The check is querried with echo_state (). + if (ip_offset == 4) // TODO: Make constexpr for first opcode offset + { + int type = m_unwind_data->m_is_script ? tree_evaluator::ECHO_SCRIPTS : tree_evaluator::ECHO_FUNCTIONS; + m_tw->push_echo_state (type, m_unwind_data->m_file); + } + + if (!m_tw->echo_state ()) + goto bail_echo; + + auto it = unwind_data->m_ip_to_tree.find (tmp_ip); + if (it == unwind_data->m_ip_to_tree.end ()) + goto bail_echo; + + tree *t = it->second; + if (!t) + goto bail_echo; + + int line = t->line (); + if (line < 0) + line = 1; + + // We don't want to echo the condition checks in for loops, but + // reset the "last echoed" line to echo the next line properly. + switch (static_cast (*ip)) + { + case INSTR::FOR_COND: + case INSTR::FOR_COMPLEX_COND: + m_echo_prior_op_was_cond = true; + goto bail_echo; + default: + break; + } + + if (m_echo_prior_op_was_cond) + { + m_echo_prior_op_was_cond = false; + m_tw->set_echo_file_pos (line); + } + + m_tw->echo_code (line); + m_tw->set_echo_file_pos (line + 1); + } +bail_echo: + // TODO: Check all trees one time and cache the result somewhere? // Until another bp is set? Debugging will be quite slow // with one check for each op-code. - auto it = unwind_data->m_ip_to_tree.find (tmp_ip); - - if (it == unwind_data->m_ip_to_tree.end ()) - goto debug_check_end; - - bool is_ret = *ip == static_cast (INSTR::RET); - - m_sp = sp; - m_bsp = bsp; - m_rsp = rsp; - m_code = code; - m_data = data; - m_name_data = name_data; - m_ip = tmp_ip; - m_unwind_data = unwind_data; - m_tw->set_active_bytecode_ip (tmp_ip); - - tree *t = it->second; - - // do_breakpoint will check if there is a breakpoint attached - // to the relevant code and escape to the debugger repl - // if neccessary. - if (t) - { - try + if (m_tw->debug_mode ()) + { + auto it = unwind_data->m_ip_to_tree.find (tmp_ip); + + if (it == unwind_data->m_ip_to_tree.end ()) + goto debug_check_end; + + bool is_ret = *ip == static_cast (INSTR::RET); + + m_sp = sp; + m_bsp = bsp; + m_rsp = rsp; + m_code = code; + m_data = data; + m_name_data = name_data; + m_ip = tmp_ip; + m_unwind_data = unwind_data; + m_tw->set_active_bytecode_ip (tmp_ip); + + tree *t = it->second; + + // do_breakpoint will check if there is a breakpoint attached + // to the relevant code and escape to the debugger repl + // if neccessary. + if (t) { - m_tw->do_breakpoint (t->is_active_breakpoint (*m_tw), is_ret); + try + { + m_tw->do_breakpoint (t->is_active_breakpoint (*m_tw), is_ret); + } + CATCH_INTERRUPT_EXCEPTION + CATCH_INDEX_EXCEPTION + CATCH_EXECUTION_EXCEPTION + CATCH_BAD_ALLOC + CATCH_EXIT_EXCEPTION } - CATCH_INTERRUPT_EXCEPTION - CATCH_INDEX_EXCEPTION - CATCH_EXECUTION_EXCEPTION - CATCH_BAD_ALLOC - CATCH_EXIT_EXCEPTION } } debug_check_end: diff -r 56acd8f390f8 -r 02faaad763d4 libinterp/parse-tree/pt-bytecode-vm.h --- a/libinterp/parse-tree/pt-bytecode-vm.h Sat Aug 19 13:05:33 2023 +0200 +++ b/libinterp/parse-tree/pt-bytecode-vm.h Mon Aug 07 10:10:49 2023 +0200 @@ -522,6 +522,8 @@ std::string *m_name_data; unwind_data *m_unwind_data; + bool m_echo_prior_op_was_cond = false; + int m_ip; // Generic data container to recreate exceptions diff -r 56acd8f390f8 -r 02faaad763d4 libinterp/parse-tree/pt-bytecode-walk.cc --- a/libinterp/parse-tree/pt-bytecode-walk.cc Sat Aug 19 13:05:33 2023 +0200 +++ b/libinterp/parse-tree/pt-bytecode-walk.cc Mon Aug 07 10:10:49 2023 +0200 @@ -2040,6 +2040,7 @@ { m_is_script = true; + m_code.m_unwind_data.m_is_script = true; m_code.m_unwind_data.m_name = fcn.name (); m_code.m_unwind_data.m_file = fcn.fcn_file_name (); PUSH_DATA (fcn.name ()); diff -r 56acd8f390f8 -r 02faaad763d4 libinterp/parse-tree/pt-bytecode.h --- a/libinterp/parse-tree/pt-bytecode.h Sat Aug 19 13:05:33 2023 +0200 +++ b/libinterp/parse-tree/pt-bytecode.h Mon Aug 07 10:10:49 2023 +0200 @@ -233,6 +233,8 @@ unsigned m_code_size; unsigned m_ids_size; + + bool m_is_script; }; struct bytecode diff -r 56acd8f390f8 -r 02faaad763d4 libinterp/parse-tree/pt-eval.cc --- a/libinterp/parse-tree/pt-eval.cc Sat Aug 19 13:05:33 2023 +0200 +++ b/libinterp/parse-tree/pt-eval.cc Mon Aug 07 10:10:49 2023 +0200 @@ -1367,12 +1367,15 @@ || m_dbstep_flag != 0 || m_break_on_next_stmt || in_debug_repl ()); + + update_vm_dbgprofecho_flag (); } void tree_evaluator::reset_debug_state (bool mode) { m_debug_mode = mode; + update_vm_dbgprofecho_flag (); } void @@ -4850,6 +4853,7 @@ int pos) { m_echo_state = echo_this_file (file_name, type); + m_echo_file_name = file_name; m_echo_file_pos = pos; } @@ -4859,6 +4863,7 @@ int pos) { m_echo_state = state; + m_echo_file_name = file_name; m_echo_file_pos = pos; } @@ -5040,6 +5045,8 @@ if (cleanup_pushed) maybe_set_echo_state (); + update_vm_dbgprofecho_flag (); // Since m_echo might have changed value we need to call this + return octave_value (); } diff -r 56acd8f390f8 -r 02faaad763d4 libinterp/parse-tree/pt-eval.h --- a/libinterp/parse-tree/pt-eval.h Sat Aug 19 13:05:33 2023 +0200 +++ b/libinterp/parse-tree/pt-eval.h Mon Aug 07 10:10:49 2023 +0200 @@ -838,6 +838,8 @@ { int old_val = m_echo; m_echo = val; + + update_vm_dbgprofecho_flag (); return old_val; } @@ -858,6 +860,21 @@ bool debug_mode () const { return m_debug_mode; } + int echo_state () { return m_echo_state; } + + void set_echo_file_pos (int pos) + { + m_echo_file_pos = pos; + } + + void vm_set_profiler_active (bool val) + { + m_vm_profiler_active = val; + update_vm_dbgprofecho_flag (); + } + + bool vm_dbgprofecho_flag () { return m_vm_dbg_profile_echo; } + private: template @@ -973,6 +990,18 @@ std::string m_echo_file_name; + // The VM needs to keep know if the evaluation is in a debug, echo or profiler + // state. + bool m_vm_dbg_profile_echo; // Set to true if either echo, dbg or vm profiler active + bool m_vm_profiler_active; // VM specific profiler flag + + // Set m_vm_dbg_profile_echo to its proper state. Need to be done after each update to + // the underlying flags. + void update_vm_dbgprofecho_flag () + { + m_vm_dbg_profile_echo = m_debug_mode || m_echo || m_vm_profiler_active; + } + // Next line to echo, counting from 1. We use int here because the // parser does. It also initializes line and column numbers to the // invalid value -1 and that can cause trouble if cast to an