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"
50#include "pt-pr-code.h"
55#include "unwind-prot.h"
60// Initialize the singleton object
61bp_table *bp_table::instance = 0;
64snarf_file (const std::string& fname)
72 size_t sz = fs.size ();
74 std::ifstream file (fname.c_str (), std::ios::in|std::ios::binary);
78 std::string buf (sz+1, 0);
80 file.read (&buf[0], sz+1);
84 // Expected to read the entire file.
89 error ("error reading file %s", fname.c_str ());
96static std::deque<size_t>
97get_line_offsets (const std::string& buf)
99 // This could maybe be smarter. Is deque the right thing to use
102 std::deque<size_t> offsets;
104 offsets.push_back (0);
106 size_t len = buf.length ();
108 for (size_t i = 0; i < len; i++)
112 if (c == '\r' && ++i < len)
117 offsets.push_back (i+1);
119 offsets.push_back (i);
122 offsets.push_back (i+1);
125 offsets.push_back (len);
131get_file_line (const std::string& fname, size_t line)
135 static std::string last_fname;
137 static std::string buf;
139 static std::deque<size_t> offsets;
141 if (fname != last_fname)
143 buf = snarf_file (fname);
145 offsets = get_line_offsets (buf);
151 if (line < offsets.size () - 1)
153 size_t bol = offsets[line];
154 size_t eol = offsets[line+1];
156 while (eol > 0 && (buf[eol-1] == '\n' || buf[eol-1] == '\r'))
159 retval = buf.substr (bol, eol - bol);
165// Return a pointer to the user-defined function FNAME. If FNAME is
166// empty, search backward for the first user-defined function in the
167// current call stack.
169static octave_user_code *
170get_user_code (const std::string& fname = std::string ())
172 octave_user_code *dbg_fcn = 0;
175 dbg_fcn = octave_call_stack::caller_user_code ();
178 octave_value fcn = symbol_table::find_function (fname);
180 if (fcn.is_defined () && fcn.is_user_code ())
181 dbg_fcn = fcn.user_code_value ();
188parse_dbfunction_params (const char *who, const octave_value_list& args,
189 std::string& symbol_name, bp_table::intmap& lines)
191 int nargin = args.length ();
194 symbol_name = std::string ();
195 lines = bp_table::intmap ();
197 if (args.length () == 0)
200 // If we are already in a debugging function.
201 if (octave_call_stack::caller_user_code ())
204 symbol_name = get_user_code ()->name ();
206 else if (args(0).is_map ())
208 // Problem because parse_dbfunction_params() can only pass out a
211 else if (args(0).is_string())
213 symbol_name = args(0).string_value ();
219 error ("%s: invalid parameter specified", who);
221 for (int i = idx; i < nargin; i++ )
223 if (args(i).is_string ())
225 int line = atoi (args(i).string_value().c_str ());
228 lines[list_idx++] = line;
230 else if (args(i).is_map ())
231 octave_stdout << who << ": accepting a struct" << std::endl;
234 const NDArray arg = args(i).array_value ();
239 for (octave_idx_type j = 0; j < arg.nelem (); j++)
241 int line = static_cast<int> (arg.elem (j));
244 lines[list_idx++] = line;
254bp_table::do_add_breakpoint (const std::string& fname,
255 const bp_table::intmap& line)
259 octave_idx_type len = line.size ();
261 octave_user_code *dbg_fcn = get_user_code (fname);
265 tree_statement_list *cmds = dbg_fcn->body ();
269 for (int i = 0; i < len; i++)
271 const_intmap_iterator p = line.find (i);
273 if (p != line.end ())
275 int lineno = p->second;
277 retval[i] = cmds->set_breakpoint (lineno);
281 bp_set.insert (fname);
288 error ("add_breakpoint: unable to find the function requested\n");
290 tree_evaluator::debug_mode = bp_table::have_breakpoints ();
297bp_table::do_remove_breakpoint (const std::string& fname,
298 const bp_table::intmap& line)
302 octave_idx_type len = line.size ();
306 intmap results = remove_all_breakpoints_in_file (fname);
307 retval = results.size ();
311 octave_user_code *dbg_fcn = get_user_code (fname);
315 tree_statement_list *cmds = dbg_fcn->body ();
319 octave_value_list results = cmds->list_breakpoints ();
321 if (results.length () > 0)
323 for (int i = 0; i < len; i++)
325 const_intmap_iterator p = line.find (i);
327 if (p != line.end ())
328 cmds->delete_breakpoint (p->second);
331 results = cmds->list_breakpoints ();
333 bp_set_iterator it = bp_set.find (fname);
334 if (results.length () == 0 && it != bp_set.end ())
339 retval = results.length ();
343 error ("remove_breakpoint: unable to find the function requested\n");
346 tree_evaluator::debug_mode = bp_table::have_breakpoints ();
353bp_table::do_remove_all_breakpoints_in_file (const std::string& fname,
358 octave_user_code *dbg_fcn = get_user_code (fname);
362 tree_statement_list *cmds = dbg_fcn->body ();
366 octave_value_list bkpts = cmds->list_breakpoints ();
368 for (int i = 0; i < bkpts.length (); i++)
370 int lineno = static_cast<int> (bkpts(i).int_value ());
371 cmds->delete_breakpoint (lineno);
375 bp_set_iterator it = bp_set.find (fname);
376 if (it != bp_set.end ())
382 error ("remove_all_breakpoint_in_file: "
383 "unable to find the function requested\n");
385 tree_evaluator::debug_mode = bp_table::have_breakpoints ();
391bp_table::do_remove_all_breakpoints (void)
393 for (const_bp_set_iterator it = bp_set.begin (); it != bp_set.end (); it++)
394 remove_all_breakpoints_in_file (*it);
397 tree_evaluator::debug_mode = bp_table::have_breakpoints ();
401do_find_bkpt_list (octave_value_list slist,
406 for (int i = 0; i < slist.length (); i++)
408 if (slist (i).string_value () == match)
410 retval = slist(i).string_value ();
419bp_table::fname_line_map
420bp_table::do_get_breakpoint_list (const octave_value_list& fname_list)
422 fname_line_map retval;
424 for (bp_set_iterator it = bp_set.begin (); it != bp_set.end (); it++)
426 if (fname_list.length () == 0
427 || do_find_bkpt_list (fname_list, *it) != "")
429 octave_user_code *f = get_user_code (*it);
433 tree_statement_list *cmds = f->body ();
437 octave_value_list bkpts = cmds->list_breakpoints ();
438 octave_idx_type len = bkpts.length ();
442 bp_table::intmap bkpts_vec;
444 for (int i = 0; i < len; i++)
445 bkpts_vec[i] = bkpts (i).double_value ();
447 std::string symbol_name = f->name ();
449 retval[symbol_name] = bkpts_vec;
460intmap_to_ov (const bp_table::intmap& line)
464 NDArray retval (dim_vector (1, line.size ()));
466 for (size_t i = 0; i < line.size (); i++)
468 bp_table::const_intmap_iterator p = line.find (i);
470 if (p != line.end ())
472 int lineno = p->second;
473 retval(idx++) = lineno;
477 retval.resize (dim_vector (1, idx));
482DEFUN (dbstop, args, ,
484@deftypefn {Loadable Function} {@var{rline} =} dbstop (@var{func}, @var{line}, @dots{})\n\
485Set a breakpoint in a function\n\
488String representing the function name. When already in debug\n\
489mode this should be left out and only the line should be given.\n\
491Line number you would like the breakpoint to be set on. Multiple\n\
492lines might be given as separate arguments or as a vector.\n\
495The rline returned is the real line that the breakpoint was set at.\n\
496@seealso{dbclear, dbstatus, dbstep}\n\
499 bp_table::intmap retval;
500 std::string symbol_name;
501 bp_table::intmap lines;
503 parse_dbfunction_params ("dbstop", args, symbol_name, lines);
505 if (lines.size () == 0)
509 retval = bp_table::add_breakpoint (symbol_name, lines);
511 return intmap_to_ov (retval);
514DEFUN (dbclear, args, ,
516@deftypefn {Loadable Function} {} dbclear (@var{func}, @var{line}, @dots{})\n\
517Delete a breakpoint in a function\n\
520String representing the function name. When already in debug\n\
521mode this should be left out and only the line should be given.\n\
523Line number where you would like to remove the breakpoint. Multiple\n\
524lines might be given as separate arguments or as a vector.\n\
526No checking is done to make sure that the line you requested is really\n\
527a breakpoint. If you get the wrong line nothing will happen.\n\
528@seealso{dbstop, dbstatus, dbwhere}\n\
532 std::string symbol_name = "";
533 bp_table::intmap lines;
535 parse_dbfunction_params ("dbclear", args, symbol_name, lines);
538 bp_table::remove_breakpoint (symbol_name, lines);
543DEFUN (dbstatus, args, nargout,
545@deftypefn {Loadable Function} {lst =} dbstatus (@var{func})\n\
546Return a vector containing the lines on which a function has \n\
550String representing the function name. When already in debug\n\
551mode this should be left out.\n\
553@seealso{dbclear, dbwhere}\n\
557 int nargin = args.length ();
558 octave_value_list fcn_list;
559 bp_table::fname_line_map bp_list;
560 std::string symbol_name;
562 if (nargin != 0 && nargin != 1)
564 error ("dbstatus: only zero or one arguements accepted\n");
565 return octave_value ();
570 if (args(0).is_string ())
572 symbol_name = args(0).string_value ();
573 fcn_list(0) = symbol_name;
574 bp_list = bp_table::get_breakpoint_list (fcn_list);
577 gripe_wrong_type_arg ("dbstatus", args(0));
581 octave_user_code *dbg_fcn = get_user_code ();
584 symbol_name = dbg_fcn->name ();
585 fcn_list(0) = symbol_name;
588 bp_list = bp_table::get_breakpoint_list (fcn_list);
593 // Print out the breakpoint information.
595 for (bp_table::fname_line_map_iterator it = bp_list.begin ();
596 it != bp_list.end (); it++)
598 octave_stdout << "Breakpoint in " << it->first << " at line(s) ";
600 bp_table::intmap m = it->second;
602 size_t nel = m.size ();
604 for (size_t j = 0; j < nel; j++)
605 octave_stdout << m[j] << ((j < nel - 1) ? ", " : ".");
608 octave_stdout << std::endl;
610 return octave_value ();
614 // Fill in an array for return.
617 Cell names (dim_vector (bp_list.size (), 1));
618 Cell file (dim_vector (bp_list.size (), 1));
619 Cell line (dim_vector (bp_list.size (), 1));
621 for (bp_table::const_fname_line_map_iterator it = bp_list.begin ();
622 it != bp_list.end (); it++)
624 names(i) = it->first;
625 line(i) = intmap_to_ov (it->second);
626 file(i) = do_which (it->first);
630 retval.assign ("name", names);
631 retval.assign ("file", file);
632 retval.assign ("line", line);
634 return octave_value (retval);
640@deftypefn {Loadable Function} {} dbwhere ()\n\
641Show where we are in the code\n\
642@seealso{dbclear, dbstatus, dbstop}\n\
647 octave_user_code *dbg_fcn = get_user_code ();
651 bool have_file = true;
653 std::string name = dbg_fcn->fcn_file_name ();
659 name = dbg_fcn->name ();
662 octave_stdout << name << ":";
664 unwind_protect::frame_id_t uwp_frame = unwind_protect::begin_frame ();
666 unwind_protect::add_fcn (octave_call_stack::restore_frame,
667 octave_call_stack::current_frame ());
669 // Skip the frame assigned to the dbwhere function.
670 octave_call_stack::goto_frame_relative (0);
672 int l = octave_call_stack::current_line ();
676 octave_stdout << " line " << l;
678 int c = octave_call_stack::current_column ();
681 octave_stdout << ", column " << c;
683 octave_stdout << std::endl;
687 std::string line = get_file_line (name, l);
690 octave_stdout << l << ": " << line << std::endl;
694 octave_stdout << " (unknown line)\n";
696 unwind_protect::run_frame (uwp_frame);
699 error ("dbwhere: must be inside of a user function to use dbwhere\n");
704// Copied and modified from the do_type command in help.cc
705// Maybe we could share some code?
707do_dbtype (std::ostream& os, const std::string& name, int start, int end)
709 std::string ff = fcn_file_in_path (name);
713 std::ifstream fs (ff.c_str (), std::ios::in);
720 if (line >= start && line <= end)
725 if (line >= start && line <= end)
733 if (line >= start && line <= end)
739 os << "dbtype: unable to open `" << ff << "' for reading!\n";
742 os << "dbtype: unknown function " << name << "\n";
747DEFUN (dbtype, args, ,
749@deftypefn {Loadable Function} {} dbtype ()\n\
750List script file with line numbers.\n\
751@seealso{dbclear, dbstatus, dbstop}\n\
755 octave_user_code *dbg_fcn;
757 int nargin = args.length ();
758 string_vector argv = args.make_argv ("dbtype");
765 dbg_fcn = get_user_code ();
768 do_dbtype (octave_stdout, dbg_fcn->name (), 0, INT_MAX);
770 error ("dbtype: must be in a user function to give no arguments to dbtype\n");
773 case 1: // (dbtype func) || (dbtype start:end)
774 dbg_fcn = get_user_code (argv[1]);
777 do_dbtype (octave_stdout, dbg_fcn->name (), 0, INT_MAX);
780 dbg_fcn = get_user_code ();
784 std::string arg = argv[1];
786 size_t ind = arg.find (':');
788 if (ind != std::string::npos)
790 std::string start_str = arg.substr (0, ind);
791 std::string end_str = arg.substr (ind + 1);
793 int start = atoi (start_str.c_str ());
794 int end = atoi (end_str.c_str ());
796 if (std::min (start, end) <= 0)
797 error ("dbtype: start and end lines must be >= 1\n");
800 do_dbtype (octave_stdout, dbg_fcn->name (), start, end);
802 error ("dbtype: start line must be less than end line\n");
805 error ("dbtype: line specification must be `start:end'");
810 case 2: // (dbtype func start:end) , (dbtype func start)
811 dbg_fcn = get_user_code (argv[1]);
815 std::string arg = argv[2];
818 size_t ind = arg.find (':');
820 if (ind != std::string::npos)
822 std::string start_str = arg.substr (0, ind);
823 std::string end_str = arg.substr (ind + 1);
825 start = atoi (start_str.c_str ());
826 end = atoi (end_str.c_str ());
831 start = atoi (arg.c_str ());
835 if (std::min (start, end) <= 0)
836 error ("dbtype: start and end lines must be >= 1\n");
839 do_dbtype (octave_stdout, dbg_fcn->name (), start, end);
841 error ("dbtype: start line must be less than end line\n");
846 error ("dbtype: expecting zero, one, or two arguments\n");
853DEFUN (dbstack, args, nargout,
855@deftypefn {Loadable Function} {[@var{stack}, @var{idx}]} dbstack (@var{n})\n\
856Print or return current stack information. With optional argument\n\
857@var{n}, omit the @var{n} innermost stack frames.\n\
858@seealso{dbclear, dbstatus, dbstop}\n\
861 octave_value_list retval;
863 unwind_protect::frame_id_t uwp_frame = unwind_protect::begin_frame ();
865 octave_idx_type curr_frame = -1;
869 if (args.length () == 1)
873 octave_value arg = args(0);
875 if (arg.is_string ())
877 std::string s_arg = arg.string_value ();
879 n = atoi (s_arg.c_str ());
882 n = args(0).int_value ();
887 error ("dbstack: expecting N to be a nonnegative integer");
892 Octave_map stk = octave_call_stack::backtrace (nskip, curr_frame);
896 octave_idx_type nframes_to_display = stk.numel ();
898 if (nframes_to_display > 0)
900 octave_stdout << "Stopped in:\n\n";
902 Cell names = stk.contents ("name");
903 Cell lines = stk.contents ("line");
904 Cell columns = stk.contents ("column");
906 for (octave_idx_type i = 0; i < nframes_to_display; i++)
908 octave_value name = names(i);
909 octave_value line = lines(i);
910 octave_value column = columns(i);
912 octave_stdout << (i == curr_frame ? "--> " : " ")
913 << name.string_value ()
914 << " at line " << line.int_value ()
915 << " column " << column.int_value ()
922 retval(1) = curr_frame < 0 ? 1 : curr_frame + 1;
927 unwind_protect::run_frame (uwp_frame);
933do_dbupdown (const octave_value_list& args, const std::string& who)
937 if (args.length () == 1)
939 octave_value arg = args(0);
941 if (arg.is_string ())
943 std::string s_arg = arg.string_value ();
945 n = atoi (s_arg.c_str ());
948 n = args(0).int_value ();
956 if (! octave_call_stack::goto_frame_relative (n, true))
957 error ("%s: invalid stack frame", who.c_str ());
963@deftypefn {Loadable Function} {} dbup\n\
964@deftypefnx {Loadable Function} {} dbup (@var{n})\n\
965In debugging mode, move up the execution stack @var{n} frames.\n\
966If @var{n} is omitted, move up one frame.\n\
972 do_dbupdown (args, "dbup");
977DEFUN (dbdown, args, ,
979@deftypefn {Loadable Function} {} dbdown\n\
980@deftypefnx {Loadable Function} {} dbdown (@var{n})\n\
981In debugging mode, move down the execution stack @var{n} frames.\n\
982If @var{n} is omitted, move down one frame.\n\
988 do_dbupdown (args, "dbdown");
993DEFUN (dbstep, args, ,
995@deftypefn {Command} {} dbstep\n\
996@deftypefnx {Command} {} dbstep @var{n}\n\
997@deftypefnx {Command} {} dbstep in\n\
998@deftypefnx {Command} {} dbstep out\n\
999In debugging mode, execute the next @var{n} lines of code.\n\
1000If @var{n} is omitted , execute the next single line of code.\n\
1001If the next line of code is itself\n\
1002defined in terms of an m-file remain in the existing function.\n\
1004Using @code{dbstep in} will cause execution of the next line to step into\n\
1005any m-files defined on the next line. Using @code{dbstep out} will cause\n\
1006execution to continue until the current function returns.\n\
1007@seealso{dbcont, dbquit}\n\
1012 int nargin = args.length ();
1016 else if (nargin == 1)
1018 if (args(0).is_string ())
1020 std::string arg = args(0).string_value ();
1028 tree_evaluator::dbstep_flag = -1;
1030 else if (arg == "out")
1034 tree_evaluator::dbstep_flag = -2;
1038 int n = atoi (arg.c_str ());
1044 tree_evaluator::dbstep_flag = n;
1047 error ("dbstep: invalid argument");
1052 error ("dbstep: expecting character string as argument");
1058 tree_evaluator::dbstep_flag = 1;
1062 error ("dbstep: can only be called in debug mode");
1064 return octave_value_list ();
1067DEFALIAS (dbnext, dbstep);
1069DEFUN (dbcont, args, ,
1071@deftypefn {Command} {} dbcont\n\
1072In debugging mode, quit debugging mode and continue execution.\n\
1073@seealso{dbstep, dbquit}\n\
1078 if (args.length () == 0)
1082 tree_evaluator::dbstep_flag = 0;
1088 error ("dbcont: can only be called in debug mode");
1090 return octave_value_list ();
1093DEFUN (dbquit, args, ,
1095@deftypefn {Command} {} dbquit\n\
1096In debugging mode, quit debugging mode and return to the top level.\n\
1097@seealso{dbstep, dbcont}\n\
1102 if (args.length () == 0)
1104 tree_evaluator::dbstep_flag = 0;
1106 octave_throw_interrupt_exception ();
1112 error ("dbquit: can only be called in debug mode");
1114 return octave_value_list ();
1117DEFUN (isdebugmode, args, ,
1119@deftypefn {Loadable Function} {} isdebugmode ()\n\
1120Return true if debug mode is on, otherwise false.\n\
1121@seealso{dbstack, dbclear, dbstop, dbstatus}\n\
1124 octave_value retval;
1126 if (args.length () == 0)
1127 retval = Vdebugging;
1135;;; Local Variables: ***