Mercurial > jwe > octave
changeset 27040:8408acb7ca4f
make dbup/dbdown work again (bug #56020)
Store call stack index in stack frame object to make iteration in call
stack possible given only a stack frame. Store current debug frame in
call stack and use that info to track stack location when at a debug
prompt. New class for managing debug info and the debug repl.
It should now be possible to enter the debugger recursively.
* stack-frame.h, stack-frame.cc (stack_frame::m_index): New data member.
(stack_frame::m_prev): Delete data member and all uses.
Update constructors for base and derived classes and change all uses.
(stack_frame::index): New function.
(display_stopped_in_message): New function.
* bp-table.cc: Eliminate use of Vdebugger. Don't set debug_mode directly.
* error.cc (maybe_enter_debugger, warning_1): Use
tree_evaluator::enter_debugger instead of input_system::keyboard to
enter debugger.
* call-stack.h, call-stack.cc (call_stack::goto_frame_relative):
Delete.
(call_stack::find_current_user_frame,
call_stack::find_current_user_frame,
call_stack::find_current_user_frame, call_stack::find_caller_frame):
New functions.
(call_stack::goto_caller_frame): Simplify by calling find_caller_frame
and dbupdown.
(class stack_trace_generator): Delete class and all uses.
(call_stack::current_user_code): Rename from caller_user_code and
eliminate nskip argument.
(call_stack::current_user_code_line):
Rename from caller_user_code_line.
(call_stack::current_user_code_column):
Rename from caller_user_code_column.
(call_stack::goto_frame, call_stack::dbupdown):
Use stack_frame::display_in_stopped_message.
(call_stack::backtrace_frames, call_stack::backtrace):
Make these functions work again. Eliminate nskip argument.
* debug.cc (Fdbstop, Fdbclear, Fdbstep):
Call tree_evaluator::reset_debug_state.
(Fdbstop, Fdbclear, Fdbstep, Fdbcont, Fdbquit, Fisdebugmode):
Move real work to evaluator, debugger, and call_stack classes.
(Fdbwhere): Use stack_frame::display_stopped_in_message.
* input.h, input.cc (Vdebugging): Delete variable and all uses.
(input_system::keyboard, execute_in_debugger_handler,
input_system::get_debutg_input): Delete. Move functionality to
new debugger class.
(Fkeyboard): Simplify.
(do_keyboard): Delete.
* pt-eval.h, pt-eval.cc (class debugger): New class for managing debugger.
(tree_evaluator::m_debug_frame): Rename from m_current_frame. Use
this variable to manage debugger location when making function calls
during debugging (for example, to dbup, dbdown, etc.).
(tree_evaluator::m_debugger_stack): New variable.
(tree_evaluator::reset_debug_state): Set m_debug_mode if we have
breakpoints, are stepping in the debugger, or executing in the
debugger repl.
(tree_evaluator::reset_debug_state): Don't set m_dbstep_flag here.
(tree_evaluator::enter_debugger): New function.
(tree_evaluator::do_breakpoint): Handle exiting from the debugger
object here. Maybe rejoin existing debugger instance.
(tree_evaluator::do_keyboard): Delete.
(bool tree_evaluator::in_debug_repl, bool
tree_evaluator::exit_debug_repl, bool tree_evaluator::exit_debug_repl,
bool tree_evaluator::abort_debug_repl, bool
tree_evaluator::abort_debug_repl): New functions.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Mon, 08 Apr 2019 00:36:33 +0000 |
parents | ca0230d3efbf |
children | 43f6f02dd91c |
files | libgui/src/m-editor/file-editor-tab.cc libinterp/corefcn/call-stack.cc libinterp/corefcn/call-stack.h libinterp/corefcn/debug.cc libinterp/corefcn/error.cc libinterp/corefcn/help.cc libinterp/corefcn/input.cc libinterp/corefcn/input.h libinterp/corefcn/stack-frame.cc libinterp/corefcn/stack-frame.h libinterp/parse-tree/bp-table.cc libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-eval.h |
diffstat | 13 files changed, 767 insertions(+), 698 deletions(-) [+] |
line wrap: on
line diff
--- a/libgui/src/m-editor/file-editor-tab.cc Mon Apr 08 09:22:39 2019 -0700 +++ b/libgui/src/m-editor/file-editor-tab.cc Mon Apr 08 00:36:33 2019 +0000 @@ -2085,10 +2085,9 @@ // If this file is loaded, check that we aren't currently running it bool retval = true; octave_idx_type curr_frame = -1; - size_t nskip = 0; call_stack& cs = __get_call_stack__ ("file_editor_tab::exit_debug_and_clear"); - octave_map stk = cs.backtrace (nskip, curr_frame, false); + octave_map stk = cs.backtrace (curr_frame, false); Cell names = stk.contents ("name"); for (octave_idx_type i = names.numel () - 1; i >= 0; i--) { @@ -2109,7 +2108,7 @@ while (names.numel () > i) { octave::sleep (0.01); - stk = cs.backtrace (nskip, curr_frame, false); + stk = cs.backtrace (curr_frame, false); names = stk.contents ("name"); } }
--- a/libinterp/corefcn/call-stack.cc Mon Apr 08 09:22:39 2019 -0700 +++ b/libinterp/corefcn/call-stack.cc Mon Apr 08 00:36:33 2019 +0000 @@ -24,6 +24,8 @@ # include "config.h" #endif +#include <iostream> + #include "lo-regexp.h" #include "str-vec.h" @@ -54,86 +56,6 @@ namespace octave { - class stack_trace_generator : public stack_frame_walker - { - public: - - stack_trace_generator (size_t nskip = 0) - : stack_frame_walker (), m_frames (), m_nskip (nskip), - m_curr_frame (0) - { } - - stack_trace_generator (const stack_trace_generator&) = delete; - - stack_trace_generator& operator = (const stack_trace_generator&) = delete; - - ~stack_trace_generator (void) = default; - - std::list<stack_frame *> frames (void) const { return m_frames; } - - size_t current_frame (void) const { return m_curr_frame; } - - void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame) - { - stack_frame *slink = frame.static_link (); - - if (slink) - slink->accept (*this); - } - - void visit_script_stack_frame (script_stack_frame& frame) - { - maybe_add_frame (frame); - - stack_frame *slink = frame.static_link (); - - if (slink) - slink->accept (*this); - } - - void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame) - { - maybe_add_frame (frame); - - symbol_scope scope = frame.get_scope (); - - stack_frame *slink = frame.static_link (); - - if (slink) - slink->accept (*this); - } - - void visit_scope_stack_frame (scope_stack_frame& frame) - { - symbol_scope scope = frame.get_scope (); - - stack_frame *slink = frame.static_link (); - - if (slink) - slink->accept (*this); - } - - private: - - void maybe_add_frame (stack_frame& frame) - { - if (m_nskip > 0) - { - m_nskip--; - return; - } - - m_frames.push_back (&frame); - } - - std::list<stack_frame *> m_frames; - - // Number of user code frames to skip. - size_t m_nskip; - - size_t m_curr_frame; - }; - call_stack::call_stack (tree_evaluator& evaluator) : m_evaluator (evaluator), m_cs (), m_curr_frame (0), m_max_stack_depth (1024), m_global_values () @@ -167,42 +89,32 @@ return retval; } - octave_user_code * call_stack::caller_user_code (size_t nskip) const + octave_user_code * call_stack::current_user_code (void) const { - octave_user_code *retval = nullptr; + // Start at current frame. - size_t xframe = m_curr_frame; + size_t xframe = find_current_user_frame (); - while (xframe != 0) + if (xframe > 0) { const stack_frame *elt = m_cs[xframe]; octave_function *f = elt->function (); if (f && f->is_user_code ()) - { - if (nskip > 0) - nskip--; - else - { - retval = dynamic_cast<octave_user_code *> (f); - break; - } - } - - xframe = m_cs[xframe]->previous (); + return dynamic_cast<octave_user_code *> (f); } - return retval; + return nullptr; } - int call_stack::caller_user_code_line (void) const + int call_stack::current_user_code_line (void) const { - int retval = -1; + // Start at current frame. - size_t xframe = m_curr_frame; + size_t xframe = find_current_user_frame (); - while (xframe != 0) + if (xframe > 0) { const stack_frame *elt = m_cs[xframe]; @@ -210,24 +122,47 @@ if (f && f->is_user_code ()) { - if (elt->line () > 0) - { - retval = elt->line (); - break; - } + int line = elt->line (); + + if (line > 0) + return line; } - - xframe = m_cs[xframe]->previous (); } - return retval; + return -1; + } + + int call_stack::current_user_code_column (void) const + { + // Start at current frame. + + size_t xframe = find_current_user_frame (); + + if (xframe > 0) + { + const stack_frame *elt = m_cs[xframe]; + + octave_function *f = elt->function (); + + if (f && f->is_user_code ()) + { + int column = elt->column (); + + if (column > 0) + return column; + } + } + + return -1; } unwind_protect * call_stack::curr_fcn_unwind_protect_frame (void) const { - size_t xframe = m_curr_frame; + // Start at current frame. - while (xframe != 0) + size_t xframe = find_current_user_frame (); + + if (xframe > 0) { const stack_frame *elt = m_cs[xframe]; @@ -235,40 +170,11 @@ if (f && f->is_user_code ()) return elt->unwind_protect_frame (); - - xframe = m_cs[xframe]->previous (); } return nullptr; } - int call_stack::caller_user_code_column (void) const - { - int retval = -1; - - size_t xframe = m_curr_frame; - - while (xframe != 0) - { - const stack_frame *elt = m_cs[xframe]; - - octave_function *f = elt->function (); - - if (f && f->is_user_code ()) - { - if (elt->column ()) - { - retval = elt->column (); - break; - } - } - - xframe = m_cs[xframe]->previous (); - } - - return retval; - } - octave_user_code * call_stack::debug_user_code (void) const { octave_user_code *retval = nullptr; @@ -444,7 +350,10 @@ stack_frame *slink = get_static_link (prev_frame); - m_cs.push_back (new scope_stack_frame (*this, prev_frame, scope, slink)); + stack_frame *new_frame + = new scope_stack_frame (*this, scope, m_curr_frame, slink); + + m_cs.push_back (new_frame); } void call_stack::push (octave_user_function *fcn, unwind_protect *up_frame, @@ -459,9 +368,11 @@ stack_frame *slink = get_static_link (prev_frame); - m_cs.push_back (new user_fcn_stack_frame (*this, fcn, up_frame, - prev_frame, slink, - closure_frames)); + stack_frame *new_frame + = new user_fcn_stack_frame (*this, fcn, up_frame, m_curr_frame, + slink, closure_frames); + + m_cs.push_back (new_frame); } void call_stack::push (octave_user_script *script, unwind_protect *up_frame) @@ -475,8 +386,10 @@ stack_frame *slink = get_static_link (prev_frame); - m_cs.push_back (new script_stack_frame (*this, script, up_frame, - prev_frame, slink)); + stack_frame *new_frame + = new script_stack_frame (*this, script, up_frame, m_curr_frame, slink); + + m_cs.push_back (new_frame); } void call_stack::push (octave_function *fcn) @@ -490,8 +403,10 @@ stack_frame *slink = get_static_link (prev_frame); - m_cs.push_back (new compiled_fcn_stack_frame (*this, fcn, prev_frame, - slink)); + stack_frame *new_frame + = new compiled_fcn_stack_frame (*this, fcn, m_curr_frame, slink); + + m_cs.push_back (new_frame); } bool call_stack::goto_frame (size_t n, bool verbose) @@ -504,111 +419,156 @@ m_curr_frame = n; - const stack_frame *elt = m_cs[n]; + if (verbose) + { + const stack_frame *elt = m_cs[n]; - if (verbose) - octave_stdout << "stopped in " << elt->fcn_name () - << " at line " << elt->line () - << " column " << elt->column () - << " [" << elt->fcn_file_name () << "] " - << std::endl; + elt->display_stopped_in_message (octave_stdout); + } } return retval; } - bool call_stack::goto_frame_relative (int nskip, bool verbose) + size_t call_stack::find_current_user_frame (void) const + { + size_t user_frame = m_curr_frame; + + stack_frame *frm = m_cs[user_frame]; + + if (! (frm->is_user_fcn_frame () || frm->is_user_script_frame () + || frm->is_scope_frame ())) + { + frm = frm->static_link (); + + user_frame = frm->index (); + } + + return user_frame; + } + + stack_frame *call_stack::current_user_frame (void) const + { + size_t frame = find_current_user_frame (); + + return m_cs[frame]; + } + + // Go to the Nth frame (up if N is negative or down if positive) in + // the call stack that corresponds to a script, function, or scope + // beginning with the frame indexed by START. + + size_t call_stack::dbupdown (size_t start, int n, bool verbose) { - bool retval = false; + if (start >= m_cs.size ()) + error ("invalid stack frame"); + + // Can't go up from here. + + if (start == 0 && n < 0) + { + if (verbose) + m_cs[start]->display_stopped_in_message (octave_stdout); + + return start; + } + + stack_frame *frm = m_cs[start]; + + if (! (frm && (frm->is_user_fcn_frame () + || frm->is_user_script_frame () + || frm->is_scope_frame ()))) + error ("call_stack::dbupdown: invalid initial frame in call stack!"); + + // Use index into the call stack to begin the search. At this point + // we iterate up or down using indexing instead of static links + // because ... FIXME: it's a bit complicated, but deserves + // explanation. May be easiest with some pictures of the call stack + // for an example or two. + + size_t xframe = frm->index (); + + if (n == 0) + { + if (verbose) + frm->display_stopped_in_message (octave_stdout); + + return xframe; + } int incr = 0; - if (nskip < 0) - incr = -1; - else if (nskip > 0) + if (n < 0) + { + incr = -1; + n = -n; + } + else if (n > 0) incr = 1; - size_t xframe = m_curr_frame; + size_t last_good_frame = 0; while (true) { - if ((incr < 0 && xframe == 0) || (incr > 0 && xframe == m_cs.size () - 1)) - break; + frm = m_cs[xframe]; + + if (frm->is_user_fcn_frame () || frm->is_user_script_frame () + || frm->is_scope_frame ()) + { + last_good_frame = xframe; + + if (n == 0) + break; + + n--; + } xframe += incr; - const stack_frame *elt = m_cs[xframe]; - - octave_function *f = elt->function (); - - if (xframe == 0 || (f && f->is_user_code ())) + if (xframe == 0) { - if (nskip > 0) - nskip--; - else if (nskip < 0) - nskip++; - - if (nskip == 0) - { - m_curr_frame = xframe; + last_good_frame = 0; + break; + } - if (verbose) - { - std::ostringstream buf; - - if (f) - buf << "stopped in " << elt->fcn_name () - << " at line " << elt->line () - << " [" << elt->fcn_file_name () << "] " - << std::endl; - else - buf << "at top level" << std::endl; - - octave_stdout << buf.str (); - } - - retval = true; - break; - } - } - else if (incr == 0) // Break out of infinite loop by choosing an incr. - incr = -1; + if (xframe == m_cs.size ()) + break; } - return retval; + if (verbose) + m_cs[last_good_frame]->display_stopped_in_message (octave_stdout); + + return last_good_frame; } - size_t call_stack::find_caller_frame (void) - { - // Find the preceeding frame that corresponds to a script or - // function. Expected to be called from a stack frame corresponding - // to a compiled function. - - size_t xframe = m_curr_frame; - - bool skipped = false; - - while (xframe != 0) - { - xframe = m_cs[xframe]->previous (); + // Like dbupdown above but find the starting frame automatically from + // the current frame. If the current frame is already a user + // function, script, or scope frame, use that. Otherwise, follow + // the static link for the current frame. If that is not a user + // function, script or scope frame then there is an error in the + // implementation. - stack_frame *frm = m_cs[xframe]; - if (frm->is_user_fcn_frame () || frm->is_user_script_frame ()) - { - if (! skipped) - // We found the current user code frame, so skip it. - skipped = true; - else - return xframe; - } - } + size_t call_stack::dbupdown (int n, bool verbose) + { + size_t start = find_current_user_frame (); - return 0; + return dbupdown (start, n, verbose); } + // May be used to temporarily change the value ov m_curr_frame inside + // a function like evalin. If used in a function like dbup, the new + // value of m_curr_frame would be wiped out when dbup returns and the + // stack frame for dbup is popped. + void call_stack::goto_caller_frame (void) { - m_curr_frame = find_caller_frame (); + size_t start = find_current_user_frame (); + + // FIXME: is this supposed to be an error? + if (start == 0) + error ("already at top level"); + + m_curr_frame = dbupdown (start, -1, false); } void call_stack::goto_base_frame (void) @@ -618,58 +578,51 @@ } std::list<stack_frame *> - call_stack::backtrace_frames (size_t nskip, - octave_idx_type& curr_user_frame) const + call_stack::backtrace_frames (octave_idx_type& curr_user_frame) const { - stack_trace_generator stack_tracer (nskip); - - // Start at the end of the stack, even if the current pointer is - // somewhere else. + std::list<stack_frame *> frames; - size_t n = m_cs.size () - 1; - - m_cs[n]->accept (stack_tracer); + // curr_frame is the index to the current frame in the overall call + // stack, which includes any compiled function frames and scope + // frames. The curr_user_frame value we set is the index into the + // subset of frames returned in the octave_map object. - std::list<stack_frame *> frame_list = stack_tracer.frames (); - - if (frame_list.empty ()) - return frame_list; + size_t curr_frame = find_current_user_frame (); - // Find the index into the list of frames where we are currently. - // We'll just search the list of frames for the one that matches - // where we are now. + // Don't include top-level stack frame in the list. - stack_frame *frame = m_cs[m_curr_frame]; - - octave_function *fcn = frame->function (); + for (size_t n = m_cs.size () - 1; n > 0; n--) + { + stack_frame *frm = m_cs[n]; - if (! (fcn && fcn->is_user_code ())) - frame = frame->static_link (); + if (frm->is_user_script_frame () || frm->is_user_fcn_frame () + || frm->is_scope_frame ()) + { + if (frm->index () == curr_frame) + curr_user_frame = frames.size (); - curr_user_frame = 0; - bool found = false; - for (const auto *frm : frame_list) - { - if (frm == frame) - { - found = true; - break; + frames.push_back (frm); } - curr_user_frame++; + if (n == 0) + break; } - if (! found) - curr_user_frame = -1; - - return frame_list; + return frames; } - octave_map call_stack::backtrace (size_t nskip, - octave_idx_type& curr_user_frame, + std::list<stack_frame *> + call_stack::backtrace_frames (void) const + { + octave_idx_type curr_user_frame = -1; + + return backtrace_frames (curr_user_frame); + } + + octave_map call_stack::backtrace (octave_idx_type& curr_user_frame, bool print_subfn) const { - std::list<stack_frame *> frames = backtrace_frames (nskip, curr_user_frame); + std::list<stack_frame *> frames = backtrace_frames (curr_user_frame); size_t nframes = frames.size (); @@ -684,22 +637,26 @@ for (const auto *frm : frames) { - file(k) = frm->fcn_file_name (); - name(k) = frm->fcn_name (print_subfn); - line(k) = frm->line (); - column(k) = frm->column (); + if (frm->is_user_script_frame () || frm->is_user_fcn_frame () + || frm->is_scope_frame ()) + { + file(k) = frm->fcn_file_name (); + name(k) = frm->fcn_name (print_subfn); + line(k) = frm->line (); + column(k) = frm->column (); - k++; + k++; + } } return retval; } - octave_map call_stack::backtrace (size_t nskip) + octave_map call_stack::backtrace (void) const { octave_idx_type curr_user_frame = -1; - return backtrace (nskip, curr_user_frame, true); + return backtrace (curr_user_frame, true); } octave_map call_stack::empty_backtrace (void) const @@ -712,11 +669,13 @@ // Never pop top scope. // FIXME: is it possible for this case to happen? - if (m_cs.size () > 0) + if (m_cs.size () > 1) { stack_frame *elt = m_cs.back (); - m_curr_frame = elt->previous (); + stack_frame *caller = elt->static_link (); + + m_curr_frame = caller->index (); m_cs.pop_back ();
--- a/libinterp/corefcn/call-stack.h Mon Apr 08 09:22:39 2019 -0700 +++ b/libinterp/corefcn/call-stack.h Mon Apr 08 00:36:33 2019 +0000 @@ -135,15 +135,15 @@ } // User code caller. - octave_user_code * caller_user_code (size_t nskip = 0) const; + octave_user_code * current_user_code (void) const; unwind_protect * curr_fcn_unwind_protect_frame (void) const; // Line in user code caller. - int caller_user_code_line (void) const; + int current_user_code_line (void) const; // Column in user code caller. - int caller_user_code_column (void) const; + int current_user_code_column (void) const; // Current function that we are debugging. octave_user_code * debug_user_code (void) const; @@ -214,28 +214,25 @@ goto_frame (n); } - bool goto_frame_relative (int n, bool verbose = false); + size_t find_current_user_frame (void) const; + stack_frame *current_user_frame (void) const; - size_t find_caller_frame (void); + size_t dbupdown (size_t start, int n, bool verbose); + size_t dbupdown (int n = -1, bool verbose = false); void goto_caller_frame (void); void goto_base_frame (void); std::list<stack_frame *> - backtrace_frames (size_t nskip, octave_idx_type& curr_user_frame) const; + backtrace_frames (octave_idx_type& curr_user_frame) const; - std::list<stack_frame *> backtrace_frames (size_t nskip = 0) const - { - octave_idx_type curr_user_frame = -1; + std::list<stack_frame *> backtrace_frames (void) const; - return backtrace_frames (nskip, curr_user_frame); - } - - octave_map backtrace (size_t nskip, octave_idx_type& curr_user_frame, + octave_map backtrace (octave_idx_type& curr_user_frame, bool print_subfn = true) const; - octave_map backtrace (size_t nskip = 0); + octave_map backtrace (void) const; octave_map empty_backtrace (void) const;
--- a/libinterp/corefcn/debug.cc Mon Apr 08 09:22:39 2019 -0700 +++ b/libinterp/corefcn/debug.cc Mon Apr 08 00:36:33 2019 +0000 @@ -257,6 +257,9 @@ } } + // If we add a breakpoint, we also need to reset debug_mode. + tw.reset_debug_state (); + return retval; } @@ -328,6 +331,9 @@ bptab.remove_breakpoint (symbol_name, lines); } + // If we remove a breakpoint, we also need to reset debug_mode. + tw.reset_debug_state (); + return ovl (); } @@ -401,7 +407,7 @@ else { /* - if (Vdebugging) + if (tw.in_debug_repl ()) { octave_user_code *dbg_fcn = tw.get_user_code (); if (dbg_fcn) @@ -561,42 +567,11 @@ @seealso{dbstack, dblist, dbstatus, dbcont, dbstep, dbup, dbdown} @end deftypefn */) { - octave::tree_evaluator& tw = interp.get_evaluator (); - - octave_user_code *dbg_fcn = tw.get_user_code (); - - if (! dbg_fcn) - { - octave_stdout << "stopped at top level" << std::endl; - return ovl (); - } - - octave_stdout << "stopped in " << dbg_fcn->name () << " at "; - octave::call_stack& cs = interp.get_call_stack (); - int l = cs.debug_user_code_line (); - - if (l > 0) - { - octave_stdout << "line " << l; - - std::string file_name = dbg_fcn->fcn_file_name (); + octave::stack_frame *frm = cs.current_user_frame (); - if (! file_name.empty ()) - { - octave_stdout << " [" << file_name << ']' << std::endl; - - std::string line = dbg_fcn->get_code_line (l); - - if (! line.empty ()) - octave_stdout << l << ": " << line << std::endl; - } - else - octave_stdout << std::endl; - } - else - octave_stdout << "<unknown line>" << std::endl; + frm->display_stopped_in_message (octave_stdout); return ovl (); } @@ -909,10 +884,10 @@ if (nargout == 0) { - octave_map stk = cs.backtrace (nskip, curr_frame); - octave_idx_type nframes_to_display = stk.numel (); + octave_map stk = cs.backtrace (curr_frame); + octave_idx_type nframes = stk.numel (); - if (nframes_to_display > 0) + if (nframes > 0) { octave::preserve_stream_state stream_state (os); @@ -926,14 +901,14 @@ size_t max_name_len = 0; - for (octave_idx_type i = 0; i < nframes_to_display; i++) + for (octave_idx_type i = nskip; i < nframes; i++) { std::string name = names(i).string_value (); max_name_len = std::max (name.length (), max_name_len); } - for (octave_idx_type i = 0; i < nframes_to_display; i++) + for (octave_idx_type i = nskip; i < nframes; i++) { std::string name = names(i).string_value (); std::string file = files(i).string_value (); @@ -955,7 +930,7 @@ } else { - octave_map stk = cs.backtrace (nskip, curr_frame, false); + octave_map stk = cs.backtrace (curr_frame, false); // If current stack frame is not in the list curr_frame will be // -1 and either nskip caused us to skip it or we are at the top @@ -1050,10 +1025,9 @@ if (who == "dbup") n = -n; - octave::call_stack& cs = interp.get_call_stack (); + octave::tree_evaluator& tw = interp.get_evaluator (); - if (! cs.goto_frame_relative (n, true)) - error ("%s: invalid stack frame", who.c_str ()); + tw.dbupdown (n, true); } DEFMETHOD (dbup, interp, args, , @@ -1109,7 +1083,9 @@ @seealso{dbcont, dbquit} @end deftypefn */) { - if (! Vdebugging) + octave::tree_evaluator& tw = interp.get_evaluator (); + + if (! tw.in_debug_repl ()) error ("dbstep: can only be called in debug mode"); int nargin = args.length (); @@ -1117,45 +1093,37 @@ if (nargin > 1) print_usage (); - octave::tree_evaluator& tw = interp.get_evaluator (); + int n = 0; if (nargin == 1) { - std::string arg = args(0).xstring_value ("dbstep: input argument must be a string"); + std::string arg + = args(0).xstring_value ("dbstep: input argument must be a string"); if (arg == "in") - { - Vdebugging = false; - Vtrack_line_num = true; - - tw.set_dbstep_flag (-1); - } + n = -1; else if (arg == "out") - { - Vdebugging = false; - Vtrack_line_num = true; - - tw.set_dbstep_flag (-2); - } + n = -2; else { - int n = atoi (arg.c_str ()); + n = atoi (arg.c_str ()); if (n < 1) error ("dbstep: invalid argument"); - - Vdebugging = false; - Vtrack_line_num = true; - - tw.set_dbstep_flag (n); } } else + n = 1; + + if (n != 0) { - Vdebugging = false; Vtrack_line_num = true; - tw.set_dbstep_flag (1); + tw.set_dbstep_flag (n); + + // If we set the dbstep flag, we also need to reset debug_mode. + tw.reset_debug_state (); + } return ovl (); @@ -1170,18 +1138,17 @@ @seealso{dbstep, dbquit} @end deftypefn */) { - if (! Vdebugging) + octave::tree_evaluator& tw = interp.get_evaluator (); + + if (! tw.in_debug_repl ()) error ("dbcont: can only be called in debug mode"); if (args.length () != 0) print_usage (); - Vdebugging = false; Vtrack_line_num = true; - octave::tree_evaluator& tw = interp.get_evaluator (); - - tw.reset_debug_state (); + tw.exit_debug_repl (true); return ovl (); } @@ -1194,21 +1161,15 @@ @seealso{dbcont, dbstep} @end deftypefn */) { - if (! Vdebugging) + octave::tree_evaluator& tw = interp.get_evaluator (); + + if (! tw.in_debug_repl ()) error ("dbquit: can only be called in debug mode"); if (args.length () != 0) print_usage (); - // FIXME: there are too many debug mode flags! - - Vdebugging = false; - - octave::tree_evaluator& tw = interp.get_evaluator (); - - tw.reset_debug_state (false); - - throw octave::interrupt_exception (); + tw.abort_debug_repl (true); return ovl (); } @@ -1223,7 +1184,9 @@ if (args.length () != 0) print_usage (); - return ovl (Vdebugging); + octave::tree_evaluator& tw = octave::__get_evaluator__ ("Fisdebugmode"); + + return ovl (tw.in_debug_repl ()); } DEFMETHOD (__db_next_breakpoint_quiet__, interp, args, ,
--- a/libinterp/corefcn/error.cc Mon Apr 08 09:22:39 2019 -0700 +++ b/libinterp/corefcn/error.cc Mon Apr 08 00:36:33 2019 +0000 @@ -218,7 +218,7 @@ Vlast_error_id = id; Vlast_error_message = base_msg; - octave_user_code *fcn = cs.caller_user_code (); + octave_user_code *fcn = cs.current_user_code (); if (fcn) Vlast_error_stack = cs.backtrace (); @@ -357,7 +357,7 @@ || octave::application::forced_interactive ()) && ((Vdebug_on_error && bptab.debug_on_err (last_error_id ())) || (Vdebug_on_caught && bptab.debug_on_caught (last_error_id ()))) - && cs.caller_user_code ()) + && cs.current_user_code ()) { octave::unwind_protect frame; frame.protect_var (Vdebug_on_error); @@ -366,9 +366,6 @@ octave::tree_evaluator& tw = octave::__get_evaluator__ ("maybe_enter_debugger"); - tw.debug_mode (true); - tw.current_frame (cs.current_frame ()); - if (show_stack_trace) { std::string stack_trace = e.info (); @@ -381,10 +378,7 @@ } } - octave::input_system& input_sys - = octave::__get_input_system__ ("maybe_enter_debugger"); - - input_sys.keyboard (); + tw.enter_debugger (); } } @@ -527,7 +521,7 @@ octave::call_stack& cs = octave::__get_call_stack__ ("error_1"); - bool in_user_code = cs.caller_user_code () != nullptr; + bool in_user_code = cs.current_user_code () != nullptr; if (in_user_code && ! discard_error_messages) show_stack_trace = true; @@ -749,7 +743,7 @@ octave::call_stack& cs = octave::__get_call_stack__ ("warning_1"); - bool in_user_code = cs.caller_user_code () != nullptr; + bool in_user_code = cs.current_user_code () != nullptr; if (! fmt_suppresses_backtrace && in_user_code && Vbacktrace_on_warning @@ -770,13 +764,7 @@ octave::tree_evaluator& tw = octave::__get_evaluator__ ("warning_1"); - tw.debug_mode (true); - tw.current_frame (cs.current_frame ()); - - octave::input_system& input_sys - = octave::__get_input_system__ ("warning_1"); - - input_sys.keyboard (); + tw.enter_debugger (); } } }
--- a/libinterp/corefcn/help.cc Mon Apr 08 09:22:39 2019 -0700 +++ b/libinterp/corefcn/help.cc Mon Apr 08 00:36:33 2019 +0000 @@ -489,7 +489,7 @@ call_stack& cs = m_interpreter.get_call_stack (); - octave_user_code *curr_fcn = cs.caller_user_code (); + octave_user_code *curr_fcn = cs.current_user_code (); if (! curr_fcn) return retval;
--- a/libinterp/corefcn/input.cc Mon Apr 08 09:22:39 2019 -0700 +++ b/libinterp/corefcn/input.cc Mon Apr 08 00:36:33 2019 +0000 @@ -86,9 +86,6 @@ // the next user prompt. bool Vdrawnow_requested = false; -// TRUE if we are in debugging mode. -bool Vdebugging = false; - // TRUE if we are recording line numbers in a source file. // Always true except when debugging and taking input directly from // the terminal. @@ -635,53 +632,15 @@ retval = m_interpreter.eval_string (input_buf, true, parse_status, nargout); - if (! Vdebugging && retval.empty ()) + tree_evaluator& tw = m_interpreter.get_evaluator (); + + if (! tw.in_debug_repl () && retval.empty ()) retval(0) = Matrix (); } return retval; } - octave_value input_system::keyboard (const octave_value_list& args) - { - octave_value retval; - - int nargin = args.length (); - - assert (nargin == 0 || nargin == 1); - - octave::unwind_protect frame; - - frame.add_fcn (octave::command_history::ignore_entries, - octave::command_history::ignoring_entries ()); - - octave::command_history::ignore_entries (false); - - frame.protect_var (Vdebugging); - - octave::call_stack& cs = m_interpreter.get_call_stack (); - - frame.add_method (cs, &octave::call_stack::restore_frame, - cs.current_frame ()); - - // FIXME: probably we just want to print one line, not the - // entire statement, which might span many lines... - // - // tree_print_code tpc (octave_stdout); - // stmt.accept (tpc); - - Vdebugging = true; - Vtrack_line_num = false; - - std::string prompt = "debug> "; - if (nargin > 0) - prompt = args(0).string_value (); - - get_debug_input (prompt); - - return retval; - } - bool input_system::have_input_event_hooks (void) const { return ! m_input_event_hook_functions.empty (); @@ -729,153 +688,6 @@ return retval; } - static void - execute_in_debugger_handler (const std::pair<std::string, int>& arg) - { - octave_link::execute_in_debugger_event (arg.first, arg.second); - } - - void input_system::get_debug_input (const std::string& prompt) - { - octave::unwind_protect frame; - - octave::tree_evaluator& tw = m_interpreter.get_evaluator (); - - bool silent = tw.quiet_breakpoint_flag (false); - - octave::call_stack& cs = m_interpreter.get_call_stack (); - - octave_user_code *caller = cs.caller_user_code (); - std::string nm; - int curr_debug_line; - - if (caller) - { - nm = caller->fcn_file_name (); - - if (nm.empty ()) - nm = caller->name (); - - curr_debug_line = cs.caller_user_code_line (); - } - else - curr_debug_line = cs.current_line (); - - std::ostringstream buf; - - if (! nm.empty ()) - { - if (m_gud_mode) - { - static char ctrl_z = 'Z' & 0x1f; - - buf << ctrl_z << ctrl_z << nm << ':' << curr_debug_line; - } - else - { - // FIXME: we should come up with a clean way to detect - // that we are stopped on the no-op command that marks the - // end of a function or script. - - if (! silent) - { - buf << "stopped in " << nm; - - if (curr_debug_line > 0) - buf << " at line " << curr_debug_line; - } - - octave_link::enter_debugger_event (nm, curr_debug_line); - - octave_link::set_workspace (); - - frame.add_fcn (execute_in_debugger_handler, - std::pair<std::string, int> (nm, curr_debug_line)); - - if (! silent) - { - std::string line_buf; - - if (caller) - line_buf = caller->get_code_line (curr_debug_line); - - if (! line_buf.empty ()) - buf << "\n" << curr_debug_line << ": " << line_buf; - } - } - } - - if (silent) - octave::command_editor::erase_empty_line (true); - - std::string msg = buf.str (); - - if (! msg.empty ()) - std::cerr << msg << std::endl; - - frame.add_method (*this, &octave::input_system::set_PS1, m_PS1); - m_PS1 = prompt; - - // FIXME: should debugging be possible in an embedded interpreter? - - octave::application *app = octave::application::app (); - - if (! app->interactive ()) - { - - frame.add_method (app, &octave::application::interactive, - app->interactive ()); - - frame.add_method (app, &octave::application::forced_interactive, - app->forced_interactive ()); - - app->interactive (true); - - app->forced_interactive (true); - } - - octave::parser curr_parser (m_interpreter); - - while (Vdebugging) - { - try - { - Vtrack_line_num = false; - - reset_error_handler (); - - curr_parser.reset (); - - int retval = curr_parser.run (); - - if (octave::command_editor::interrupt (false)) - break; - else - { - if (retval == 0 && curr_parser.m_stmt_list) - { - curr_parser.m_stmt_list->accept (tw); - - if (octave_completion_matches_called) - octave_completion_matches_called = false; - } - - octave_quit (); - } - } - catch (const octave::execution_exception& e) - { - std::string stack_trace = e.info (); - - if (! stack_trace.empty ()) - std::cerr << stack_trace; - - // Ignore errors when in debugging mode; - octave::interpreter::recover_from_exception (); - } - } - } - std::string base_reader::octave_gets (bool& eof) { octave_quit (); @@ -887,9 +699,13 @@ // Process pre input event hook function prior to flushing output and // printing the prompt. + interpreter& interp = __get_interpreter__ ("base_reader::octave_gets"); + + tree_evaluator& tw = interp.get_evaluator (); + if (application::interactive ()) { - if (! Vdebugging) + if (! tw.in_debug_repl ()) octave_link::exit_debugger_event (); octave_link::pre_input_event (); @@ -899,7 +715,7 @@ bool history_skip_auto_repeated_debugging_command = false; - input_system& input_sys = __get_input_system__ ("base_reader::octave_gets"); + input_system& input_sys = interp.get_input_system (); std::string ps = (m_pflag > 0) ? input_sys.PS1 () : input_sys.PS2 (); @@ -907,7 +723,7 @@ pipe_handler_error_count = 0; - output_system& output_sys = __get_output_system__ ("do_sync"); + output_system& output_sys = interp.get_output_system (); output_sys.reset (); @@ -920,16 +736,16 @@ if (retval != "\n" && retval.find_first_not_of (" \t\n\r") != std::string::npos) { - load_path& lp = __get_load_path__ ("base_reader::octave_gets"); + load_path& lp = interp.get_load_path (); lp.update (); - if (Vdebugging) + if (tw.in_debug_repl ()) input_sys.last_debugging_command (retval); else input_sys.last_debugging_command ("\n"); } - else if (Vdebugging) + else if (tw.in_debug_repl ()) { retval = input_sys.last_debugging_command (); history_skip_auto_repeated_debugging_command = true; @@ -1235,28 +1051,22 @@ @seealso{dbstop, dbcont, dbquit} @end deftypefn */) { - if (args.length () > 1) - print_usage (); - - octave::unwind_protect frame; + int nargin = args.length (); - octave::call_stack& cs = interp.get_call_stack (); - - frame.add_method (cs, &octave::call_stack::restore_frame, - cs.current_frame ()); - - // Go up to the nearest user code frame. - cs.goto_frame_relative (-1); + if (nargin > 1) + print_usage (); octave::tree_evaluator& tw = interp.get_evaluator (); - tw.debug_mode (true); - tw.quiet_breakpoint_flag (false); - tw.current_frame (cs.current_frame ()); + if (nargin == 1) + { + std::string prompt + = args(0).xstring_value ("keyboard: PROMPT must be a string"); - octave::input_system& input_sys = interp.get_input_system (); - - input_sys.keyboard (args); + tw.keyboard (prompt); + } + else + tw.keyboard (); return ovl (); } @@ -1625,15 +1435,6 @@ return input_sys.yes_or_no (prompt); } -octave_value -do_keyboard (const octave_value_list& args) -{ - octave::input_system& input_sys - = octave::__get_input_system__ ("do_keyboard"); - - return input_sys.keyboard (args); -} - void remove_input_event_hook_functions (void) {
--- a/libinterp/corefcn/input.h Mon Apr 08 09:22:39 2019 -0700 +++ b/libinterp/corefcn/input.h Mon Apr 08 00:36:33 2019 +0000 @@ -44,9 +44,6 @@ // the next user prompt. extern OCTINTERP_API bool Vdrawnow_requested; -// TRUE if we are in debugging mode. -extern OCTINTERP_API bool Vdebugging; - // TRUE if we are not executing a command direct from debug> prompt. extern OCTINTERP_API bool Vtrack_line_num; @@ -155,9 +152,6 @@ octave_value_list get_user_input (const octave_value_list& args, int nargout); - octave_value - keyboard (const octave_value_list& args = octave_value_list ()); - bool have_input_event_hooks (void) const; void add_input_event_hook (const hook_function& hook_fcn); @@ -196,8 +190,6 @@ hook_function_list m_input_event_hook_functions; std::string gnu_readline (const std::string& s, bool& eof) const; - - void get_debug_input (const std::string& prompt); }; class base_reader @@ -339,10 +331,6 @@ OCTAVE_DEPRECATED (5, "use 'octave::input_system::yes_or_no' instead") extern bool octave_yes_or_no (const std::string& prompt); -OCTAVE_DEPRECATED (5, "use 'octave::input_system::keyboard' instead") -extern octave_value do_keyboard (const octave_value_list& args - = octave_value_list ()); - OCTAVE_DEPRECATED (5, "use 'octave::input_system::clear_input_event_hooks' instead") extern void remove_input_event_hook_functions (void);
--- a/libinterp/corefcn/stack-frame.cc Mon Apr 08 09:22:39 2019 -0700 +++ b/libinterp/corefcn/stack-frame.cc Mon Apr 08 00:36:33 2019 +0000 @@ -478,6 +478,22 @@ accept (sc); } + void stack_frame::display_stopped_in_message (std::ostream& os) const + { + if (index () == 0) + os << "at top level" << std::endl; + else + { + os << "stopped in " << fcn_name (); + + int l = line (); + if (l > 0) + os << " at line " << line (); + + os << " [" << fcn_file_name () << "] " << std::endl; + } + } + void stack_frame::display (bool follow) const { std::ostream& os = octave_stdout; @@ -500,7 +516,7 @@ os << "line: " << m_line << std::endl; os << "column: " << m_column << std::endl; - os << "prev: " << m_prev << std::endl; + os << "index: " << m_index << std::endl; os << std::endl; @@ -543,9 +559,9 @@ script_stack_frame::script_stack_frame (call_stack& cs, octave_user_script *script, unwind_protect *up_frame, - size_t prev, + size_t index, stack_frame *static_link) - : stack_frame (cs, prev, static_link, get_access_link (static_link)), + : stack_frame (cs, index, static_link, get_access_link (static_link)), m_script (script), m_unwind_protect_frame (up_frame), m_lexical_frame_offsets (get_num_symbols (script), 1), m_value_offsets (get_num_symbols (script), 0)
--- a/libinterp/corefcn/stack-frame.h Mon Apr 08 09:22:39 2019 -0700 +++ b/libinterp/corefcn/stack-frame.h Mon Apr 08 00:36:33 2019 +0000 @@ -140,9 +140,9 @@ stack_frame (void) = delete; - stack_frame (call_stack& cs, size_t prev, stack_frame *static_link, - stack_frame *access_link) - : m_call_stack (cs), m_line (-1), m_column (-1), m_prev (prev), + stack_frame (call_stack& cs, size_t index, + stack_frame *static_link, stack_frame *access_link) + : m_call_stack (cs), m_line (-1), m_column (-1), m_index (index), m_static_link (static_link), m_access_link (access_link), m_dispatch_class () { } @@ -166,7 +166,7 @@ virtual void clear_values (void); - size_t previous (void) const { return m_prev; } + size_t index (void) const { return m_index; } void line (int l) { m_line = l; } int line (void) const { return m_line; } @@ -544,6 +544,8 @@ m_dispatch_class = class_name; } + void display_stopped_in_message (std::ostream& os) const; + virtual void mark_scope (const symbol_record&, scope_flags) = 0; virtual void display (bool follow = true) const; @@ -562,10 +564,8 @@ int m_line; int m_column; - // FIXME: We could probably eliminate this variable. Now that we - // maintain the static and access links to previous frames, this - // index should not be necessary. - size_t m_prev; + // Index in call stack. + size_t m_index; // Pointer to the nearest parent frame that contains variable // information (script, function, or scope). @@ -588,8 +588,8 @@ compiled_fcn_stack_frame (void) = delete; compiled_fcn_stack_frame (call_stack& cs, octave_function *fcn, - size_t prev, stack_frame *static_link) - : stack_frame (cs, prev, static_link, static_link->access_link ()), + size_t index, stack_frame *static_link) + : stack_frame (cs, index, static_link, static_link->access_link ()), m_fcn (fcn) { } @@ -709,7 +709,7 @@ script_stack_frame (void) = delete; script_stack_frame (call_stack& cs, octave_user_script *script, - unwind_protect *up_frame, size_t prev, + unwind_protect *up_frame, size_t index, stack_frame *static_link); script_stack_frame (const script_stack_frame& elt) = default; @@ -819,9 +819,9 @@ base_value_stack_frame (void) = delete; base_value_stack_frame (call_stack& cs, size_t num_symbols, - size_t prev, stack_frame *static_link, + size_t index, stack_frame *static_link, stack_frame *access_link) - : stack_frame (cs, prev, static_link, access_link), + : stack_frame (cs, index, static_link, access_link), m_values (num_symbols, octave_value ()), m_flags (num_symbols, LOCAL), m_auto_vars (NUM_AUTO_VARS, octave_value ()) @@ -923,10 +923,10 @@ user_fcn_stack_frame (void) = delete; user_fcn_stack_frame (call_stack& cs, octave_user_function *fcn, - unwind_protect *up_frame, size_t prev, + unwind_protect *up_frame, size_t index, stack_frame *static_link, stack_frame *access_link = nullptr) - : base_value_stack_frame (cs, get_num_symbols (fcn), prev, static_link, + : base_value_stack_frame (cs, get_num_symbols (fcn), index, static_link, (access_link ? access_link : get_access_link (fcn, static_link))), @@ -1009,9 +1009,9 @@ scope_stack_frame (void) = delete; - scope_stack_frame (call_stack& cs, size_t prev, const symbol_scope& scope, - stack_frame *static_link) - : base_value_stack_frame (cs, scope.num_symbols (), prev, + scope_stack_frame (call_stack& cs, const symbol_scope& scope, + size_t index, stack_frame *static_link) + : base_value_stack_frame (cs, scope.num_symbols (), index, static_link, nullptr), m_scope (scope) { }
--- a/libinterp/parse-tree/bp-table.cc Mon Apr 08 09:22:39 2019 -0700 +++ b/libinterp/parse-tree/bp-table.cc Mon Apr 08 00:36:33 2019 +0000 @@ -365,7 +365,7 @@ else { // It was a line number. Get function name from debugger. - if (Vdebugging) + if (m_evaluator.in_debug_repl ()) func_name = m_evaluator.get_user_code ()->profiler_name (); else error ("%s: function name must come before line number " @@ -615,7 +615,7 @@ } } - m_evaluator.debug_mode (bp_table::have_breakpoints () || Vdebugging); + m_evaluator.reset_debug_state (); return retval; } @@ -711,7 +711,7 @@ } } - m_evaluator.debug_mode (bp_table::have_breakpoints () || Vdebugging); + m_evaluator.reset_debug_state (); return retval; } @@ -745,7 +745,7 @@ error ("remove_all_breakpoint_in_file: " "unable to find function %s\n", fname.c_str ()); - m_evaluator.debug_mode (bp_table::have_breakpoints () || Vdebugging); + m_evaluator.reset_debug_state (); return retval; } @@ -762,7 +762,7 @@ remove_all_breakpoints_in_file (*it); } - m_evaluator.debug_mode (bp_table::have_breakpoints () || Vdebugging); + m_evaluator.reset_debug_state (); } std::string find_bkpt_list (octave_value_list slist, std::string match)
--- a/libinterp/parse-tree/pt-eval.cc Mon Apr 08 09:22:39 2019 -0700 +++ b/libinterp/parse-tree/pt-eval.cc Mon Apr 08 00:36:33 2019 +0000 @@ -44,6 +44,8 @@ #include "input.h" #include "interpreter-private.h" #include "interpreter.h" +#include "octave-link.h" +#include "octave.h" #include "ov-classdef.h" #include "ov-fcn-handle.h" #include "ov-usr-fcn.h" @@ -67,6 +69,230 @@ { // Normal evaluator. + class debugger + { + public: + + debugger (interpreter& interp, size_t level) + : m_interpreter (interp), m_level (level), m_in_debug_repl (false), + m_exit_debug_repl (false), m_abort_debug_repl (false) + { } + + void repl (const std::string& prompt = "debug> "); + + bool in_debug_repl (void) const { return m_in_debug_repl; } + + bool in_debug_repl (bool flag) + { + bool val = m_in_debug_repl; + m_in_debug_repl = flag; + return val; + } + + bool exit_debug_repl (void) const { return m_exit_debug_repl; } + + bool exit_debug_repl (bool flag) + { + bool val = m_exit_debug_repl; + m_exit_debug_repl = flag; + return val; + } + + bool abort_debug_repl (void) const { return m_abort_debug_repl; } + + bool abort_debug_repl (bool flag) + { + bool val = m_abort_debug_repl; + m_abort_debug_repl = flag; + return val; + } + + private: + + interpreter& m_interpreter; + + size_t m_level; + size_t m_debug_frame; + bool m_in_debug_repl; + bool m_exit_debug_repl; + bool m_abort_debug_repl; + }; + + static void + execute_in_debugger_handler (const std::pair<std::string, int>& arg) + { + octave_link::execute_in_debugger_event (arg.first, arg.second); + } + + void debugger::repl (const std::string& prompt) + { + unwind_protect frame; + + frame.protect_var (m_in_debug_repl); + + m_in_debug_repl = true; + + tree_evaluator& tw = m_interpreter.get_evaluator (); + + bool silent = tw.quiet_breakpoint_flag (false); + + call_stack& cs = m_interpreter.get_call_stack (); + + frame.add_method (cs, &call_stack::restore_frame, + cs.current_frame ()); + + cs.goto_frame (tw.debug_frame ()); + + octave_user_code *caller = cs.current_user_code (); + std::string nm; + int curr_debug_line; + + if (caller) + { + nm = caller->fcn_file_name (); + + if (nm.empty ()) + nm = caller->name (); + + curr_debug_line = cs.current_user_code_line (); + } + else + curr_debug_line = cs.current_line (); + + std::ostringstream buf; + + input_system& input_sys = m_interpreter.get_input_system (); + + if (! nm.empty ()) + { + if (input_sys.gud_mode ()) + { + static char ctrl_z = 'Z' & 0x1f; + + buf << ctrl_z << ctrl_z << nm << ':' << curr_debug_line; + } + else + { + // FIXME: we should come up with a clean way to detect + // that we are stopped on the no-op command that marks the + // end of a function or script. + + if (! silent) + { + stack_frame *frm = cs.current_user_frame (); + + frm->display_stopped_in_message (buf); + } + + octave_link::enter_debugger_event (nm, curr_debug_line); + + octave_link::set_workspace (); + + frame.add_fcn (execute_in_debugger_handler, + std::pair<std::string, int> (nm, curr_debug_line)); + + if (! silent) + { + std::string line_buf; + + if (caller) + line_buf = caller->get_code_line (curr_debug_line); + + if (! line_buf.empty ()) + buf << curr_debug_line << ": " << line_buf; + } + } + } + + if (silent) + command_editor::erase_empty_line (true); + + std::string msg = buf.str (); + + if (! msg.empty ()) + std::cerr << msg << std::endl; + + std::string tmp_prompt = prompt; + if (m_level > 0) + tmp_prompt = "[" + std::to_string (m_level) + "]" + prompt; + + frame.add_method (input_sys, &input_system::set_PS1, input_sys.PS1 ()); + input_sys.PS1 (tmp_prompt); + + // FIXME: should debugging be possible in an embedded interpreter? + + application *app = application::app (); + + if (! app->interactive ()) + { + + frame.add_method (app, &application::interactive, + app->interactive ()); + + frame.add_method (app, &application::forced_interactive, + app->forced_interactive ()); + + app->interactive (true); + + app->forced_interactive (true); + } + + parser curr_parser (m_interpreter); + + while (m_in_debug_repl) + { + if (m_exit_debug_repl || m_abort_debug_repl || tw.dbstep_flag ()) + break; + + try + { + Vtrack_line_num = false; + + reset_error_handler (); + + curr_parser.reset (); + + int retval = curr_parser.run (); + + if (command_editor::interrupt (false)) + break; + else + { + if (retval == 0 && curr_parser.m_stmt_list) + { + curr_parser.m_stmt_list->accept (tw); + + if (octave_completion_matches_called) + octave_completion_matches_called = false; + + // FIXME: the following statement is here because + // the last command may have been a dbup, dbdown, or + // dbstep command that changed the current debug + // frame. If so, we need to reset the current frame + // for the call stack. But is this right way to do + // this job? What if the statement list was + // something like "dbup; dbstack"? Will the call to + // dbstack use the right frame? If not, how can we + // fix this problem? + cs.goto_frame (tw.debug_frame ()); + } + + octave_quit (); + } + } + catch (const execution_exception& e) + { + std::string stack_trace = e.info (); + + if (! stack_trace.empty ()) + std::cerr << stack_trace; + + // Ignore errors when in debugging mode; + interpreter::recover_from_exception (); + } + } + } + bool tree_evaluator::at_top_level (void) const { return m_call_stack.at_top_level (); @@ -81,6 +307,12 @@ m_expr_result_value_list = octave_value_list (); m_lvalue_list_stack.clear (); m_nargout_stack.clear (); + + while (! m_debugger_stack.empty ()) + { + delete m_debugger_stack.top (); + m_debugger_stack.pop (); + } } int tree_evaluator::repl (bool interactive) @@ -207,7 +439,7 @@ { std::string fname; - octave_user_code *fcn = m_call_stack.caller_user_code (); + octave_user_code *fcn = m_call_stack.current_user_code (); if (fcn) { @@ -844,17 +1076,59 @@ void tree_evaluator::reset_debug_state (void) { - m_debug_mode = m_bp_table.have_breakpoints () || Vdebugging; - - m_dbstep_flag = 0; + m_debug_mode = (m_bp_table.have_breakpoints () || + m_dbstep_flag != 0 || in_debug_repl ()); } void tree_evaluator::reset_debug_state (bool mode) { m_debug_mode = mode; - - m_dbstep_flag = 0; + } + + void + tree_evaluator::enter_debugger (const std::string& prompt) + { + octave::unwind_protect frame; + + frame.add_fcn (octave::command_history::ignore_entries, + octave::command_history::ignoring_entries ()); + + octave::command_history::ignore_entries (false); + + frame.add_method (m_call_stack, &octave::call_stack::restore_frame, + m_call_stack.current_frame ()); + + // Go up to the nearest user code frame. + m_call_stack.dbupdown (0); + + // FIXME: probably we just want to print one line, not the + // entire statement, which might span many lines... + // + // tree_print_code tpc (octave_stdout); + // stmt.accept (tpc); + + Vtrack_line_num = false; + + debugger *dbgr = new debugger (m_interpreter, m_debugger_stack.size ()); + + m_debug_frame = m_call_stack.current_frame (); + + m_debugger_stack.push (dbgr); + + dbgr->repl (prompt); + } + + void + tree_evaluator::keyboard (const std::string& prompt) + { + enter_debugger (prompt); + } + + void + tree_evaluator::dbupdown (int n, bool verbose) + { + m_debug_frame = m_call_stack.dbupdown (n, verbose); } Matrix @@ -3244,11 +3518,11 @@ // Act like dbcont. - if (Vdebugging && m_call_stack.current_frame () == m_current_frame) + if (in_debug_repl () && m_call_stack.current_frame () == m_debug_frame) { - Vdebugging = false; - - reset_debug_state (); + m_dbstep_flag = 0; + + exit_debug_repl (true); } else if (m_statement_context == SC_FUNCTION || m_statement_context == SC_SCRIPT @@ -3913,17 +4187,42 @@ { bool break_on_this_statement = false; + debugger *curr_debugger + = (m_debugger_stack.empty () ? nullptr : m_debugger_stack.top ()); + + if (curr_debugger) + { + if (curr_debugger->exit_debug_repl ()) + { + // This action corresponds to dbcont. + + m_debugger_stack.pop (); + delete curr_debugger; + + reset_debug_state (); + } + else if (curr_debugger->abort_debug_repl ()) + { + // This action corresponds to dbquit. + + m_debugger_stack.pop (); + delete curr_debugger; + + debug_mode (false); + + throw interrupt_exception (); + } + } + if (is_breakpoint) { - break_on_this_statement = true; - m_dbstep_flag = 0; - m_current_frame = m_call_stack.current_frame (); + enter_debugger (); } else if (m_dbstep_flag > 0) { - if (m_call_stack.current_frame () == m_current_frame) + if (m_call_stack.current_frame () == m_debug_frame) { if (m_dbstep_flag == 1 || is_end_of_fcn_or_script) { @@ -3934,8 +4233,6 @@ // return to prompt. break_on_this_statement = true; - - m_dbstep_flag = 0; } else { @@ -3946,15 +4243,13 @@ } else if (m_dbstep_flag == 1 - && m_call_stack.current_frame () < m_current_frame) + && m_call_stack.current_frame () < m_debug_frame) { // We stepped out from the end of a function. - m_current_frame = m_call_stack.current_frame (); + m_debug_frame = m_call_stack.current_frame (); break_on_this_statement = true; - - m_dbstep_flag = 0; } } else if (m_dbstep_flag == -1) @@ -3963,9 +4258,7 @@ break_on_this_statement = true; - m_dbstep_flag = 0; - - m_current_frame = m_call_stack.current_frame (); + m_debug_frame = m_call_stack.current_frame (); } else if (m_dbstep_flag == -2) { @@ -3976,30 +4269,23 @@ // that frame. if (is_end_of_fcn_or_script - && m_call_stack.current_frame () == m_current_frame) + && m_call_stack.current_frame () == m_debug_frame) m_dbstep_flag = -1; } if (break_on_this_statement) { - input_system& input_sys = m_interpreter.get_input_system (); - - input_sys.keyboard (); + m_dbstep_flag = 0; + + // We are stepping so the debugger should already exist. If + // not, something went wrong. + if (m_debugger_stack.empty ()) + error ("internal error: dbstep without an active debugger!"); + + m_debugger_stack.top()->repl (); } } - // ARGS is currently unused, but since the do_keyboard function in - // input.cc accepts an argument list, we preserve it here so that the - // interface won't have to change if we decide to use it in the future. - - octave_value - tree_evaluator::do_keyboard (const octave_value_list& args) const - { - input_system& input_sys = m_interpreter.get_input_system (); - - return input_sys.keyboard (args); - } - bool tree_evaluator::is_logically_true (tree_expression *expr, const char *warn_for) @@ -4415,6 +4701,48 @@ return octave_value (); } + bool tree_evaluator::in_debug_repl (void) const + { + return (m_debugger_stack.empty () + ? false : m_debugger_stack.top()->in_debug_repl ()); + } + + bool tree_evaluator::in_debug_repl (bool flag) + { + if (! m_debugger_stack.empty ()) + error ("attempt to set in_debug_repl without debugger object"); + + return m_debugger_stack.top()->in_debug_repl (flag); + } + + bool tree_evaluator::exit_debug_repl (void) const + { + return (m_debugger_stack.empty () + ? false : m_debugger_stack.top()->exit_debug_repl (true)); + } + + bool tree_evaluator::exit_debug_repl (bool flag) + { + if (m_debugger_stack.empty ()) + error ("attempt to set exit_debug_repl without debugger object"); + + return m_debugger_stack.top()->exit_debug_repl (flag); + } + + bool tree_evaluator::abort_debug_repl (void) const + { + return (m_debugger_stack.empty () + ? false : m_debugger_stack.top()->abort_debug_repl ()); + } + + bool tree_evaluator::abort_debug_repl (bool flag) + { + if (m_debugger_stack.empty ()) + error ("attempt to set abort_debug_repl without debugger object"); + + return m_debugger_stack.top()->abort_debug_repl (flag); + } + octave_value tree_evaluator::PS4 (const octave_value_list& args, int nargout) { @@ -4526,7 +4854,7 @@ std::string full_name = nm; - octave_user_code *fcn = m_call_stack.caller_user_code (); + octave_user_code *fcn = m_call_stack.current_user_code (); bool found = false;
--- a/libinterp/parse-tree/pt-eval.h Mon Apr 08 09:22:39 2019 -0700 +++ b/libinterp/parse-tree/pt-eval.h Mon Apr 08 00:36:33 2019 +0000 @@ -46,6 +46,7 @@ class tree_decl_elt; class tree_expression; + class debugger; class interpreter; class unwind_protect; @@ -130,9 +131,9 @@ m_result_type (RT_UNDEFINED), m_expr_result_value (), m_expr_result_value_list (), m_lvalue_list_stack (), m_nargout_stack (), m_autoload_map (), m_bp_table (*this), - m_call_stack (*this), m_profiler (), m_current_frame (0), + m_call_stack (*this), m_profiler (), m_debug_frame (0), m_debug_mode (false), m_quiet_breakpoint_flag (false), - m_max_recursion_depth (256), + m_debugger_stack (), m_max_recursion_depth (256), m_whos_line_format (" %a:4; %ln:6; %cs:16:6:1; %rb:12; %lc:-1;\n"), m_silent_functions (false), m_string_fill_char (' '), m_PS4 ("+ "), m_dbstep_flag (0), m_echo (ECHO_OFF), @@ -289,7 +290,11 @@ void reset_debug_state (bool mode); - void set_dbstep_flag (int step) { m_dbstep_flag = step; } + void enter_debugger (const std::string& prompt = "debug> "); + + void keyboard (const std::string& prompt = "keyboard> "); + + void dbupdown (int n, bool verbose = false); // Possible types of evaluation contexts. enum stmt_list_type @@ -557,12 +562,12 @@ octave_value silent_functions (const octave_value_list& args, int nargout); - size_t current_frame (void) const { return m_current_frame; } + size_t debug_frame (void) const { return m_debug_frame; } - size_t current_frame (size_t n) + size_t debug_frame (size_t n) { - size_t val = m_current_frame; - m_current_frame = n; + size_t val = m_debug_frame; + m_debug_frame = n; return val; } @@ -593,6 +598,16 @@ return val; } + // The following functions are provided for convenience and forward + // to the corresponding functions in the debugger class for the + // current debugger (if any). + bool in_debug_repl (void) const; + bool in_debug_repl (bool flag); + bool exit_debug_repl (void) const; + bool exit_debug_repl (bool flag); + bool abort_debug_repl (void) const; + bool abort_debug_repl (bool flag); + octave_value PS4 (const octave_value_list& args, int nargout); std::string PS4 (void) const { return m_PS4; } @@ -640,6 +655,17 @@ return val; } + int dbstep_flag (void) const { return m_dbstep_flag; } + + int dbstep_flag (int val) + { + int old_val = m_dbstep_flag; + m_dbstep_flag = val; + return old_val; + } + + void set_dbstep_flag (int step) { m_dbstep_flag = step; } + octave_value echo (const octave_value_list& args, int nargout); int echo (void) const { return m_echo; } @@ -677,9 +703,6 @@ void do_breakpoint (bool is_breakpoint, bool is_end_of_fcn_or_script = false); - virtual octave_value - do_keyboard (const octave_value_list& args = octave_value_list ()) const; - bool is_logically_true (tree_expression *expr, const char *warn_for); octave_value_list @@ -730,12 +753,19 @@ profiler m_profiler; // The number of the stack frame we are currently debugging. - size_t m_current_frame; + size_t m_debug_frame; bool m_debug_mode; bool m_quiet_breakpoint_flag; + // When entering the debugger we push it on this stack. Managing + // debugger invocations this way allows us to handle recursive + // debugger calls. When we exit a debugger the object is popped + // from the stack and deleted and we resume working with the + // previous debugger (if any) that is now at the top of the stack. + std::stack<debugger *> m_debugger_stack; + // Maximum nesting level for functions, scripts, or sourced files // called recursively. int m_max_recursion_depth;