3Copyright (C) 2007, 2008, 2009 John Swensen
4Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Ben Sapp
6This file is part of Octave.
8Octave is free software; you can redistribute it and/or modify it
9under the terms of the GNU General Public License as published by the
10Free Software Foundation; either version 3 of the License, or (at your
11option) any later version.
13Octave is distributed in the hope that it will be useful, but WITHOUT
14ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18You should have received a copy of the GNU General Public License
19along with Octave; see the file COPYING. If not, see
20<http://www.gnu.org/licenses/>.
46#include "ov-usr-fcn.h"
49#include "pt-pr-code.h"
54#include "unwind-prot.h"
59// Initialize the singleton object
60bp_table *bp_table::instance = 0;
63snarf_file (const std::string& fname)
71 size_t sz = fs.size ();
73 std::ifstream file (fname.c_str (), std::ios::in|std::ios::binary);
77 std::string buf (sz+1, 0);
79 file.read (&buf[0], sz+1);
83 // Expected to read the entire file.
88 error ("error reading file %s", fname.c_str ());
95static std::deque<size_t>
96get_line_offsets (const std::string& buf)
98 // This could maybe be smarter. Is deque the right thing to use
101 std::deque<size_t> offsets;
103 offsets.push_back (0);
105 size_t len = buf.length ();
107 for (size_t i = 0; i < len; i++)
111 if (c == '\r' && ++i < len)
116 offsets.push_back (i+1);
118 offsets.push_back (i);
121 offsets.push_back (i+1);
124 offsets.push_back (len);
130get_file_line (const std::string& fname, size_t line)
134 static std::string last_fname;
136 static std::string buf;
138 static std::deque<size_t> offsets;
140 if (fname != last_fname)
142 buf = snarf_file (fname);
144 offsets = get_line_offsets (buf);
150 if (line < offsets.size () - 1)
152 size_t bol = offsets[line];
153 size_t eol = offsets[line+1];
155 while (eol > 0 && (buf[eol-1] == '\n' || buf[eol-1] == '\r'))
158 retval = buf.substr (bol, eol - bol);
164// Return a pointer to the user-defined function FNAME. If FNAME is
165// empty, search backward for the first user-defined function in the
166// current call stack.
168static octave_user_code *
169get_user_code (const std::string& fname = std::string ())
171 octave_user_code *dbg_fcn = 0;
174 dbg_fcn = octave_call_stack::caller_user_code ();
177 octave_value fcn = symbol_table::find_function (fname);
179 if (fcn.is_defined () && fcn.is_user_code ())
180 dbg_fcn = fcn.user_code_value ();
187parse_dbfunction_params (const char *who, const octave_value_list& args,
188 std::string& symbol_name, bp_table::intmap& lines)
190 int nargin = args.length ();
193 symbol_name = std::string ();
194 lines = bp_table::intmap ();
196 if (args.length () == 0)
199 // If we are already in a debugging function.
200 if (octave_call_stack::caller_user_code ())
203 symbol_name = get_user_code ()->name ();
205 else if (args(0).is_map ())
207 // Problem because parse_dbfunction_params() can only pass out a
210 else if (args(0).is_string())
212 symbol_name = args(0).string_value ();
218 error ("%s: invalid parameter specified", who);
220 for (int i = idx; i < nargin; i++ )
222 if (args(i).is_string ())
224 int line = atoi (args(i).string_value().c_str ());
227 lines[list_idx++] = line;
229 else if (args(i).is_map ())
230 octave_stdout << who << ": accepting a struct" << std::endl;
233 const NDArray arg = args(i).array_value ();
238 for (octave_idx_type j = 0; j < arg.nelem (); j++)
240 int line = static_cast<int> (arg.elem (j));
243 lines[list_idx++] = line;
253bp_table::do_add_breakpoint (const std::string& fname,
254 const bp_table::intmap& line)
258 octave_idx_type len = line.size ();
260 octave_user_code *dbg_fcn = get_user_code (fname);
264 tree_statement_list *cmds = dbg_fcn->body ();
268 for (int i = 0; i < len; i++)
270 const_intmap_iterator p = line.find (i);
272 if (p != line.end ())
274 int lineno = p->second;
276 retval[i] = cmds->set_breakpoint (lineno);
280 bp_set.insert (fname);
287 error ("add_breakpoint: unable to find the function requested\n");
289 tree_evaluator::debug_mode = bp_table::have_breakpoints () || Vdebugging;
296bp_table::do_remove_breakpoint (const std::string& fname,
297 const bp_table::intmap& line)
301 octave_idx_type len = line.size ();
305 intmap results = remove_all_breakpoints_in_file (fname);
306 retval = results.size ();
310 octave_user_code *dbg_fcn = get_user_code (fname);
314 tree_statement_list *cmds = dbg_fcn->body ();
318 octave_value_list results = cmds->list_breakpoints ();
320 if (results.length () > 0)
322 for (int i = 0; i < len; i++)
324 const_intmap_iterator p = line.find (i);
326 if (p != line.end ())
327 cmds->delete_breakpoint (p->second);
330 results = cmds->list_breakpoints ();
332 bp_set_iterator it = bp_set.find (fname);
333 if (results.length () == 0 && it != bp_set.end ())
338 retval = results.length ();
342 error ("remove_breakpoint: unable to find the function requested\n");
345 tree_evaluator::debug_mode = bp_table::have_breakpoints () || Vdebugging;
352bp_table::do_remove_all_breakpoints_in_file (const std::string& fname,
357 octave_user_code *dbg_fcn = get_user_code (fname);
361 tree_statement_list *cmds = dbg_fcn->body ();
365 octave_value_list bkpts = cmds->list_breakpoints ();
367 for (int i = 0; i < bkpts.length (); i++)
369 int lineno = static_cast<int> (bkpts(i).int_value ());
370 cmds->delete_breakpoint (lineno);
374 bp_set_iterator it = bp_set.find (fname);
375 if (it != bp_set.end ())
381 error ("remove_all_breakpoint_in_file: "
382 "unable to find the function requested\n");
384 tree_evaluator::debug_mode = bp_table::have_breakpoints () || Vdebugging;
390bp_table::do_remove_all_breakpoints (void)
392 for (const_bp_set_iterator it = bp_set.begin (); it != bp_set.end (); it++)
393 remove_all_breakpoints_in_file (*it);
396 tree_evaluator::debug_mode = bp_table::have_breakpoints () || Vdebugging;
400do_find_bkpt_list (octave_value_list slist,
405 for (int i = 0; i < slist.length (); i++)
407 if (slist (i).string_value () == match)
409 retval = slist(i).string_value ();
418bp_table::fname_line_map
419bp_table::do_get_breakpoint_list (const octave_value_list& fname_list)
421 fname_line_map retval;
423 for (bp_set_iterator it = bp_set.begin (); it != bp_set.end (); it++)
425 if (fname_list.length () == 0
426 || do_find_bkpt_list (fname_list, *it) != "")
428 octave_user_code *f = get_user_code (*it);
432 tree_statement_list *cmds = f->body ();
436 octave_value_list bkpts = cmds->list_breakpoints ();
437 octave_idx_type len = bkpts.length ();
441 bp_table::intmap bkpts_vec;
443 for (int i = 0; i < len; i++)
444 bkpts_vec[i] = bkpts (i).double_value ();
446 std::string symbol_name = f->name ();
448 retval[symbol_name] = bkpts_vec;
459intmap_to_ov (const bp_table::intmap& line)
463 NDArray retval (dim_vector (1, line.size ()));
465 for (size_t i = 0; i < line.size (); i++)
467 bp_table::const_intmap_iterator p = line.find (i);
469 if (p != line.end ())
471 int lineno = p->second;
472 retval(idx++) = lineno;
476 retval.resize (dim_vector (1, idx));
481DEFUN (dbstop, args, ,
483@deftypefn {Loadable Function} {@var{rline} =} dbstop (@var{func}, @var{line}, @dots{})\n\
484Set a breakpoint in a function\n\
487String representing the function name. When already in debug\n\
488mode this should be left out and only the line should be given.\n\
490Line number you would like the breakpoint to be set on. Multiple\n\
491lines might be given as separate arguments or as a vector.\n\
494The rline returned is the real line that the breakpoint was set at.\n\
495@seealso{dbclear, dbstatus, dbstep}\n\
498 bp_table::intmap retval;
499 std::string symbol_name;
500 bp_table::intmap lines;
502 parse_dbfunction_params ("dbstop", args, symbol_name, lines);
504 if (lines.size () == 0)
508 retval = bp_table::add_breakpoint (symbol_name, lines);
510 return intmap_to_ov (retval);
513DEFUN (dbclear, args, ,
515@deftypefn {Loadable Function} {} dbclear (@var{func}, @var{line}, @dots{})\n\
516Delete a breakpoint in a function\n\
519String representing the function name. When already in debug\n\
520mode this should be left out and only the line should be given.\n\
522Line number where you would like to remove the breakpoint. Multiple\n\
523lines might be given as separate arguments or as a vector.\n\
525No checking is done to make sure that the line you requested is really\n\
526a breakpoint. If you get the wrong line nothing will happen.\n\
527@seealso{dbstop, dbstatus, dbwhere}\n\
531 std::string symbol_name = "";
532 bp_table::intmap lines;
534 parse_dbfunction_params ("dbclear", args, symbol_name, lines);
537 bp_table::remove_breakpoint (symbol_name, lines);
542DEFUN (dbstatus, args, nargout,
544@deftypefn {Loadable Function} {lst =} dbstatus (@var{func})\n\
545Return a vector containing the lines on which a function has \n\
549String representing the function name. When already in debug\n\
550mode this should be left out.\n\
552@seealso{dbclear, dbwhere}\n\
556 int nargin = args.length ();
557 octave_value_list fcn_list;
558 bp_table::fname_line_map bp_list;
559 std::string symbol_name;
561 if (nargin != 0 && nargin != 1)
563 error ("dbstatus: only zero or one arguements accepted\n");
564 return octave_value ();
569 if (args(0).is_string ())
571 symbol_name = args(0).string_value ();
572 fcn_list(0) = symbol_name;
573 bp_list = bp_table::get_breakpoint_list (fcn_list);
576 gripe_wrong_type_arg ("dbstatus", args(0));
580 octave_user_code *dbg_fcn = get_user_code ();
583 symbol_name = dbg_fcn->name ();
584 fcn_list(0) = symbol_name;
587 bp_list = bp_table::get_breakpoint_list (fcn_list);
592 // Print out the breakpoint information.
594 for (bp_table::fname_line_map_iterator it = bp_list.begin ();
595 it != bp_list.end (); it++)
597 octave_stdout << "Breakpoint in " << it->first << " at line(s) ";
599 bp_table::intmap m = it->second;
601 size_t nel = m.size ();
603 for (size_t j = 0; j < nel; j++)
604 octave_stdout << m[j] << ((j < nel - 1) ? ", " : ".");
607 octave_stdout << std::endl;
609 return octave_value ();
613 // Fill in an array for return.
616 Cell names (dim_vector (bp_list.size (), 1));
617 Cell file (dim_vector (bp_list.size (), 1));
618 Cell line (dim_vector (bp_list.size (), 1));
620 for (bp_table::const_fname_line_map_iterator it = bp_list.begin ();
621 it != bp_list.end (); it++)
623 names(i) = it->first;
624 line(i) = intmap_to_ov (it->second);
625 file(i) = do_which (it->first);
629 retval.assign ("name", names);
630 retval.assign ("file", file);
631 retval.assign ("line", line);
633 return octave_value (retval);
639@deftypefn {Loadable Function} {} dbwhere ()\n\
640Show where we are in the code\n\
641@seealso{dbclear, dbstatus, dbstop}\n\
646 octave_user_code *dbg_fcn = get_user_code ();
650 bool have_file = true;
652 std::string name = dbg_fcn->fcn_file_name ();
658 name = dbg_fcn->name ();
661 octave_stdout << name << ":";
663 unwind_protect frame;
665 frame.add_fcn (octave_call_stack::restore_frame,
666 octave_call_stack::current_frame ());
668 // Skip the frame assigned to the dbwhere function.
669 octave_call_stack::goto_frame_relative (0);
671 int l = octave_call_stack::current_line ();
675 octave_stdout << " line " << l;
677 int c = octave_call_stack::current_column ();
680 octave_stdout << ", column " << c;
682 octave_stdout << std::endl;
686 std::string line = get_file_line (name, l);
689 octave_stdout << l << ": " << line << std::endl;
693 octave_stdout << " (unknown line)\n";
696 error ("dbwhere: must be inside of a user function to use dbwhere\n");
701// Copied and modified from the do_type command in help.cc
702// Maybe we could share some code?
704do_dbtype (std::ostream& os, const std::string& name, int start, int end)
706 std::string ff = fcn_file_in_path (name);
710 std::ifstream fs (ff.c_str (), std::ios::in);
717 if (line >= start && line <= end)
722 if (line >= start && line <= end)
730 if (line >= start && line <= end)
736 os << "dbtype: unable to open `" << ff << "' for reading!\n";
739 os << "dbtype: unknown function " << name << "\n";
744DEFUN (dbtype, args, ,
746@deftypefn {Loadable Function} {} dbtype ()\n\
747List script file with line numbers.\n\
748@seealso{dbclear, dbstatus, dbstop}\n\
752 octave_user_code *dbg_fcn;
754 int nargin = args.length ();
755 string_vector argv = args.make_argv ("dbtype");
762 dbg_fcn = get_user_code ();
765 do_dbtype (octave_stdout, dbg_fcn->name (), 0, INT_MAX);
767 error ("dbtype: must be in a user function to give no arguments to dbtype\n");
770 case 1: // (dbtype func) || (dbtype start:end)
771 dbg_fcn = get_user_code (argv[1]);
774 do_dbtype (octave_stdout, dbg_fcn->name (), 0, INT_MAX);
777 dbg_fcn = get_user_code ();
781 std::string arg = argv[1];
783 size_t ind = arg.find (':');
785 if (ind != std::string::npos)
787 std::string start_str = arg.substr (0, ind);
788 std::string end_str = arg.substr (ind + 1);
790 int start = atoi (start_str.c_str ());
791 int end = atoi (end_str.c_str ());
793 if (std::min (start, end) <= 0)
794 error ("dbtype: start and end lines must be >= 1\n");
797 do_dbtype (octave_stdout, dbg_fcn->name (), start, end);
799 error ("dbtype: start line must be less than end line\n");
802 error ("dbtype: line specification must be `start:end'");
807 case 2: // (dbtype func start:end) , (dbtype func start)
808 dbg_fcn = get_user_code (argv[1]);
812 std::string arg = argv[2];
815 size_t ind = arg.find (':');
817 if (ind != std::string::npos)
819 std::string start_str = arg.substr (0, ind);
820 std::string end_str = arg.substr (ind + 1);
822 start = atoi (start_str.c_str ());
823 end = atoi (end_str.c_str ());
828 start = atoi (arg.c_str ());
832 if (std::min (start, end) <= 0)
833 error ("dbtype: start and end lines must be >= 1\n");
836 do_dbtype (octave_stdout, dbg_fcn->name (), start, end);
838 error ("dbtype: start line must be less than end line\n");
843 error ("dbtype: expecting zero, one, or two arguments\n");
850DEFUN (dbstack, args, nargout,
852@deftypefn {Loadable Function} {[@var{stack}, @var{idx}]} dbstack (@var{n})\n\
853Print or return current stack information. With optional argument\n\
854@var{n}, omit the @var{n} innermost stack frames.\n\
855@seealso{dbclear, dbstatus, dbstop}\n\
858 octave_value_list retval;
860 unwind_protect frame;
862 octave_idx_type curr_frame = -1;
866 if (args.length () == 1)
870 octave_value arg = args(0);
872 if (arg.is_string ())
874 std::string s_arg = arg.string_value ();
876 n = atoi (s_arg.c_str ());
879 n = args(0).int_value ();
884 error ("dbstack: expecting N to be a nonnegative integer");
889 Octave_map stk = octave_call_stack::backtrace (nskip, curr_frame);
893 octave_idx_type nframes_to_display = stk.numel ();
895 if (nframes_to_display > 0)
897 octave_stdout << "Stopped in:\n\n";
899 Cell names = stk.contents ("name");
900 Cell lines = stk.contents ("line");
901 Cell columns = stk.contents ("column");
903 for (octave_idx_type i = 0; i < nframes_to_display; i++)
905 octave_value name = names(i);
906 octave_value line = lines(i);
907 octave_value column = columns(i);
909 octave_stdout << (i == curr_frame ? "--> " : " ")
910 << name.string_value ()
911 << " at line " << line.int_value ()
912 << " column " << column.int_value ()
919 retval(1) = curr_frame < 0 ? 1 : curr_frame + 1;
928do_dbupdown (const octave_value_list& args, const std::string& who)
932 if (args.length () == 1)
934 octave_value arg = args(0);
936 if (arg.is_string ())
938 std::string s_arg = arg.string_value ();
940 n = atoi (s_arg.c_str ());
943 n = args(0).int_value ();
951 if (! octave_call_stack::goto_frame_relative (n, true))
952 error ("%s: invalid stack frame", who.c_str ());
958@deftypefn {Loadable Function} {} dbup\n\
959@deftypefnx {Loadable Function} {} dbup (@var{n})\n\
960In debugging mode, move up the execution stack @var{n} frames.\n\
961If @var{n} is omitted, move up one frame.\n\
967 do_dbupdown (args, "dbup");
972DEFUN (dbdown, args, ,
974@deftypefn {Loadable Function} {} dbdown\n\
975@deftypefnx {Loadable Function} {} dbdown (@var{n})\n\
976In debugging mode, move down the execution stack @var{n} frames.\n\
977If @var{n} is omitted, move down one frame.\n\
983 do_dbupdown (args, "dbdown");
988DEFUN (dbstep, args, ,
990@deftypefn {Command} {} dbstep\n\
991@deftypefnx {Command} {} dbstep @var{n}\n\
992@deftypefnx {Command} {} dbstep in\n\
993@deftypefnx {Command} {} dbstep out\n\
994In debugging mode, execute the next @var{n} lines of code.\n\
995If @var{n} is omitted , execute the next single line of code.\n\
996If the next line of code is itself\n\
997defined in terms of an m-file remain in the existing function.\n\
999Using @code{dbstep in} will cause execution of the next line to step into\n\
1000any m-files defined on the next line. Using @code{dbstep out} will cause\n\
1001execution to continue until the current function returns.\n\
1002@seealso{dbcont, dbquit}\n\
1007 int nargin = args.length ();
1011 else if (nargin == 1)
1013 if (args(0).is_string ())
1015 std::string arg = args(0).string_value ();
1023 tree_evaluator::dbstep_flag = -1;
1025 else if (arg == "out")
1029 tree_evaluator::dbstep_flag = -2;
1033 int n = atoi (arg.c_str ());
1039 tree_evaluator::dbstep_flag = n;
1042 error ("dbstep: invalid argument");
1047 error ("dbstep: expecting character string as argument");
1053 tree_evaluator::dbstep_flag = 1;
1057 error ("dbstep: can only be called in debug mode");
1059 return octave_value_list ();
1062DEFALIAS (dbnext, dbstep);
1064DEFUN (dbcont, args, ,
1066@deftypefn {Command} {} dbcont\n\
1067In debugging mode, quit debugging mode and continue execution.\n\
1068@seealso{dbstep, dbquit}\n\
1073 if (args.length () == 0)
1077 tree_evaluator::reset_debug_state ();
1083 error ("dbcont: can only be called in debug mode");
1085 return octave_value_list ();
1088DEFUN (dbquit, args, ,
1090@deftypefn {Command} {} dbquit\n\
1091In debugging mode, quit debugging mode and return to the top level.\n\
1092@seealso{dbstep, dbcont}\n\
1097 if (args.length () == 0)
1101 tree_evaluator::reset_debug_state ();
1103 octave_throw_interrupt_exception ();
1109 error ("dbquit: can only be called in debug mode");
1111 return octave_value_list ();
1114DEFUN (isdebugmode, args, ,
1116@deftypefn {Loadable Function} {} isdebugmode ()\n\
1117Return true if debug mode is on, otherwise false.\n\
1118@seealso{dbstack, dbclear, dbstop, dbstatus}\n\
1121 octave_value retval;
1123 if (args.length () == 0)
1124 retval = Vdebugging;