GNU Octave  8.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
debug.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2001-2023 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <deque>
31 #include <fstream>
32 #include <iomanip>
33 #include <iostream>
34 #include <limits>
35 #include <set>
36 #include <string>
37 
38 #include "dNDArray.h"
39 
40 #include "bp-table.h"
41 #include "defun.h"
42 #include "error.h"
43 #include "errwarn.h"
44 #include "file-ops.h"
45 #include "help.h"
46 #include "input.h"
47 #include "interpreter-private.h"
48 #include "interpreter.h"
49 #include "lo-sysdep.h"
51 #include "ov-usr-fcn.h"
52 #include "ov.h"
53 #include "ovl.h"
54 #include "pager.h"
55 #include "parse.h"
56 #include "pt-eval.h"
57 #include "unwind-prot.h"
58 #include "utils.h"
59 #include "utils.h"
60 #include "variables.h"
61 
63 
64 static octave_value
65 bp_lines_to_ov (const octave::bp_table::bp_lines& lines)
66 {
67  int idx = 0;
68 
69  NDArray retval (dim_vector (1, lines.size ()));
70 
71  for (const auto& lineno : lines)
72  retval(idx++) = lineno;
73 
74  retval.resize (dim_vector (1, idx));
75 
76  return retval;
77 }
78 
79 DEFMETHOD (dbstop, interp, args, ,
80  doc: /* -*- texinfo -*-
81 @deftypefn {} {} dbstop @var{fcn}
82 @deftypefnx {} {} dbstop @var{fcn} @var{line}
83 @deftypefnx {} {} dbstop @var{fcn} @var{line1} @var{line2} @dots{}
84 @deftypefnx {} {} dbstop @var{line1} @dots{}
85 @deftypefnx {} {} dbstop in @var{fcn}
86 @deftypefnx {} {} dbstop in @var{fcn} at @var{line}
87 @deftypefnx {} {} dbstop in @var{fcn} at @var{line} if "@var{condition}"
88 @deftypefnx {} {} dbstop in @var{class} at @var{method}
89 @deftypefnx {} {} dbstop if @var{event}
90 @deftypefnx {} {} dbstop if @var{event} @var{ID}
91 @deftypefnx {} {} dbstop (@var{bp_struct})
92 @deftypefnx {} {@var{rline} =} dbstop @dots{}
93 
94 Set breakpoints for the built-in debugger.
95 
96 @var{fcn} is the name of a function on the current @code{path}. When
97 already in debug mode the @var{fcn} argument can be omitted and the current
98 function will be used. Breakpoints at subfunctions are set with the scope
99 operator @samp{>}. For example, If @file{file.m} has a subfunction
100 @code{fcn2}, then a breakpoint in @code{fcn2} can be specified by
101 @code{file>fcn2}.
102 
103 @var{line} is the line number at which to break. If @var{line} is not
104 specified, it defaults to the first executable line in the file
105 @file{fcn.m}. Multiple lines can be specified in a single command; when
106 function syntax is used, the lines may also be passed as a single vector
107 argument (@code{[@var{line1}, @var{line2}, @dots{}]}).
108 
109 @var{condition} is any Octave expression that can be evaluated in the code
110 context that exists at the breakpoint. When the breakpoint is encountered,
111 @var{condition} will be evaluated, and execution will stop if
112 @var{condition} is true. If @var{condition} cannot be evaluated, for
113 example because it refers to an undefined variable, an error will be thrown.
114 Expressions with side effects (such as @code{y++ > 1}) will alter variables,
115 and should generally be avoided. Conditions containing quotes (@samp{"},
116 @samp{'}) or comment characters (@samp{#}, @samp{%}) must be enclosed in
117 quotes. (This does not apply to conditions entered from the editor's context
118 menu.) For example:
119 
120 @example
121 dbstop in axis at 246 if 'any (opt == "x")'
122 @end example
123 
124 The form specifying @var{event} does not cause a specific breakpoint at a given
125 function and line number. Instead it causes debug mode to be entered when
126 certain unexpected events are encountered. Possible values are
127 
128 @table @code
129 @item error
130 Stop when an error is reported. This is equivalent to specifying both
131 @code{debug_on_error (true)} and @code{debug_on_interrupt (true)}.
132 
133 @item caught error
134 Stop when an error is caught by a try-catch block (not yet implemented).
135 
136 @item interrupt
137 Stop when an interrupt (@kbd{Ctrl-C}) occurs.
138 
139 @item naninf
140 Stop when code returns a non-finite value (not yet implemented).
141 
142 @item warning
143 Stop when a warning is reported. This is equivalent to specifying
144 @code{debug_on_warning (true)}.
145 @end table
146 
147 The events @code{error}, @code{caught error}, and @code{warning} can all be
148 followed by a string specifying an error ID or warning ID@. If that is done,
149 only errors with the specified ID will cause execution to stop. To stop on one
150 of a set of IDs, multiple @code{dbstop} commands must be issued.
151 
152 Breakpoints and events can be removed using the @code{dbclear} command with
153 the same syntax.
154 
155 It is possible to save all breakpoints and restore them at once by issuing
156 the commands @code{bp_state = dbstatus; @dots{}; dbstop (bp_state)}.
157 
158 The optional output @var{rline} is the real line number where the breakpoint
159 was set. This can differ from the specified line if the line is not
160 executable. For example, if a breakpoint attempted on a blank line then Octave
161 will set the real breakpoint at the next executable line.
162 
163 When a file is re-parsed, such as when it is modified outside the GUI, all
164 breakpoints within the file are cleared.
165 
166 @seealso{dbclear, dbstatus, dbstep, debug_on_error, debug_on_warning,
167 debug_on_interrupt}
168 @end deftypefn */)
169 {
170  octave::bp_table::bp_lines retmap;
171  std::string symbol_name = ""; // stays empty for "dbstop if error" etc
172  std::string class_name = "";
173  octave::bp_table::bp_lines lines;
174  std::string condition = "";
175  octave_value retval;
176 
177  octave::tree_evaluator& tw = interp.get_evaluator ();
178 
179  octave::bp_table& bptab = tw.get_bp_table ();
180 
181  if (args.length() >= 1 && ! args(0).isstruct ())
182  {
183  // explicit function / line / condition
184  bptab.parse_dbfunction_params ("dbstop", args, symbol_name,
185  class_name, lines, condition);
186 
187  if (lines.size () == 0)
188  lines.insert (1);
189 
190  if (symbol_name != "")
191  {
192  retmap = bptab.add_breakpoints_in_function (symbol_name, class_name,
193  lines, condition);
194  retval = bp_lines_to_ov (retmap);
195  }
196  }
197  else if (args.length () != 1)
198  {
199  print_usage ();
200  }
201  else // structure of the form output by dbstatus
202  {
203  octave_map mv = args(0).map_value ();
204  if (mv.isfield ("bkpt") || mv.isfield ("errs") || mv.isfield ("warn")
205  || mv.isfield ("intr"))
206  {
207  bptab.dbstop_process_map_args (mv);
208 
209  // Replace mv by "bkpt", to use the processing below.
210  octave_value bkpt = mv.getfield ("bkpt");
211  if (bkpt.isempty ())
212  mv = octave_map ();
213  else
214  {
215  if (bkpt.iscell () && bkpt.cell_value ().numel () > 0
216  && bkpt.cell_value () (0).isstruct ())
217  mv = bkpt.cell_value () (0).map_value ();
218  else
219  error ("dbstop: invalid 'bkpt' field");
220  }
221  }
222  if (mv.isempty ())
223  {
224  // no changes requested. Occurs if "errs" non-empty but "bkpt" empty
225  }
226  else if (! mv.isfield ("name") || ! mv.isfield ("line"))
227  {
228  error ("dbstop: Cell array must contain fields 'name' and 'line'");
229  }
230  else
231  {
232  bool use_cond = mv.isfield ("cond");
233  Cell name = mv.getfield ("name");
234  Cell line = mv.getfield ("line");
235  Cell cond = (use_cond ? mv.getfield ("cond") : Cell ());
236  std::string unconditional = "";
237  for (octave_idx_type i = 0; i < line.numel (); i++)
238  {
239  lines.insert (line(i).int_value ());
240  bptab.add_breakpoints_in_function (name(i).string_value (),
241  "", lines,
242  (use_cond
243  ? cond(i).string_value ()
244  : unconditional));
245  }
246  retval = octave_value (line.numel ());
247  }
248  }
249 
250  // If we add a breakpoint, we also need to reset debug_mode.
251  tw.reset_debug_state ();
252 
253  return retval;
254 }
255 
256 DEFMETHOD (dbclear, interp, args, ,
257  doc: /* -*- texinfo -*-
258 @deftypefn {} {} dbclear @var{fcn}
259 @deftypefnx {} {} dbclear @var{fcn} @var{line}
260 @deftypefnx {} {} dbclear @var{fcn} @var{line1} @var{line2} @dots{}
261 @deftypefnx {} {} dbclear @var{line} @dots{}
262 @deftypefnx {} {} dbclear all
263 @deftypefnx {} {} dbclear in @var{fcn}
264 @deftypefnx {} {} dbclear in @var{fcn} at @var{line}
265 @deftypefnx {} {} dbclear if @var{event}
266 @deftypefnx {} {} dbclear ("@var{fcn}")
267 @deftypefnx {} {} dbclear ("@var{fcn}", @var{line})
268 @deftypefnx {} {} dbclear ("@var{fcn}", @var{line1}, @var{line2}, @dots{})
269 @deftypefnx {} {} dbclear ("@var{fcn}", @var{line1}, @dots{})
270 @deftypefnx {} {} dbclear (@var{line}, @dots{})
271 @deftypefnx {} {} dbclear ("all")
272 Delete a breakpoint at line number @var{line} in the function @var{fcn}.
273 
274 Arguments are
275 
276 @table @var
277 @item fcn
278 Function name as a string variable. When already in debug mode this
279 argument can be omitted and the current function will be used.
280 
281 @item line
282 Line number from which to remove a breakpoint. Multiple lines may be given
283 as separate arguments or as a vector.
284 
285 @item event
286 An event such as @code{error}, @code{interrupt}, or @code{warning}
287 (@pxref{XREFdbstop,,@code{dbstop}} for details).
288 @end table
289 
290 When called without a line number specification all breakpoints in the named
291 function are cleared.
292 
293 If the requested line is not a breakpoint no action is performed.
294 
295 The special keyword @qcode{"all"} will clear all breakpoints from all
296 files.
297 @seealso{dbstop, dbstatus, dbwhere}
298 @end deftypefn */)
299 {
300  std::string symbol_name = ""; // stays empty for "dbclear if error" etc
301  std::string class_name = "";
302  octave::bp_table::bp_lines lines;
303  std::string dummy; // "if" condition -- only used for dbstop
304 
305  octave::tree_evaluator& tw = interp.get_evaluator ();
306 
307  octave::bp_table& bptab = tw.get_bp_table ();
308 
309  bptab.parse_dbfunction_params ("dbclear", args, symbol_name, class_name,
310  lines, dummy);
311 
312  if (args.length () == 1 && symbol_name == "all")
313  {
314  bptab.remove_all_breakpoints ();
315  bptab.dbclear_all_signals ();
316  }
317  else
318  {
319  if (symbol_name != "")
320  bptab.remove_breakpoints_from_function (symbol_name, lines);
321  }
322 
323  // If we remove a breakpoint, we also need to reset debug_mode.
324  tw.reset_debug_state ();
325 
326  return ovl ();
327 }
328 
329 DEFMETHOD (dbstatus, interp, args, nargout,
330  doc: /* -*- texinfo -*-
331 @deftypefn {} {} dbstatus
332 @deftypefnx {} {} dbstatus @var{fcn}
333 @deftypefnx {} {@var{bp_list} =} dbstatus @dots{}
334 Report the location of active breakpoints.
335 
336 When called with no input or output arguments, print the list of all
337 functions with breakpoints and the line numbers where those breakpoints are
338 set.
339 
340 If a function name @var{fcn} is specified then only report breakpoints
341 for the named function and its subfunctions.
342 
343 The optional return argument @var{bp_list} is a struct array with the
344 following fields:
345 
346 @table @asis
347 @item name
348 The name of the function with a breakpoint. A subfunction, say @code{fcn2}
349 within an m-file, say @file{file.m}, is specified as @code{file>fcn2}.
350 
351 @item file
352 The name of the m-file where the function code is located.
353 
354 @item line
355 The line number with the breakpoint.
356 
357 @item cond
358 The condition that must be satisfied for the breakpoint to be active, or
359 the empty string for unconditional breakpoints.
360 @end table
361 
362 @c Note: When @code{dbstatus} is called from the debug prompt within a function,
363 @c the list of breakpoints is automatically trimmed to the breakpoints in the
364 @c current function.
365 If @code{dbstop if error} is true but no explicit IDs are specified, the
366 return value will have an empty field called @qcode{"errs"}. If IDs are
367 specified, the @code{errs} field will have one row per ID@. If
368 @code{dbstop if error} is false, there is no @qcode{"errs"} field.
369 The @qcode{"warn"} field is set similarly by @code{dbstop if warning}.
370 
371 @seealso{dbstop, dbclear, dbwhere, dblist, dbstack}
372 @end deftypefn */)
373 {
374  int nargin = args.length ();
375 
376  if (nargin != 0 && nargin != 1)
377  error ("dbstatus: only zero or one arguments accepted\n");
378 
379  octave_value_list fcn_list;
380  octave::bp_table::fname_bp_map bp_list;
381  std::string symbol_name;
382 
383  octave::tree_evaluator& tw = interp.get_evaluator ();
384 
385  octave::bp_table& bptab = tw.get_bp_table ();
386 
387  if (nargin == 1)
388  {
389  if (! args(0).is_string ())
390  err_wrong_type_arg ("dbstatus", args(0));
391 
392  symbol_name = args(0).string_value ();
393  fcn_list(0) = symbol_name;
394  bp_list = bptab.get_breakpoint_list (fcn_list);
395  }
396  else
397  {
398  /*
399  if (tw.in_debug_repl ())
400  {
401  octave_user_code *dbg_fcn = tw.get_user_code ();
402  if (dbg_fcn)
403  {
404  symbol_name = dbg_fcn->name ();
405  fcn_list(0) = symbol_name;
406  }
407  }
408  */
409 
410  bp_list = bptab.get_breakpoint_list (fcn_list);
411  }
412 
413  if (nargout == 0)
414  {
415  // Print out the breakpoint information.
416 
417  for (auto& fnm_bp_p: bp_list)
418  {
419  std::list<octave::bp_type> m = fnm_bp_p.second;
420 
421  // print unconditional breakpoints, if any, on a single line
422 
423  // first, check to see if there are any
424  int have_unconditional = 0;
425  for (const auto& bp : m)
426  {
427  if (bp.cond == "")
428  {
429  if (have_unconditional++)
430  break; // stop once we know its plural
431  }
432  }
433  // If we actually have some, print line numbers only
434  if (have_unconditional)
435  {
436  const char *_s_ = (have_unconditional > 1) ? "s" : "";
437  octave_stdout << "breakpoint" << _s_ << " in " << fnm_bp_p.first
438  << " at line" << _s_ << ' ';
439 
440  for (const auto& bp : m)
441  {
442  if (bp.cond == "")
443  octave_stdout << bp.line << ' ';
444  }
445  octave_stdout << std::endl;
446  }
447 
448  // print conditional breakpoints, one per line, with conditions
449  for (const auto& bp : m)
450  {
451  if (bp.cond != "")
452  octave_stdout << "breakpoint in " << fnm_bp_p.first
453  << " at line " << bp.line
454  << " if " << bp.cond << "\n";
455  }
456  }
457 
458  bptab.stop_on_err_warn_status (true);
459 
460  return ovl ();
461  }
462  else
463  {
464  octave::help_system& help_sys = interp.get_help_system ();
465 
466  // Fill in an array for return.
467  int i = 0;
468  octave_map retmap;
469  octave_value retval;
470 
471  // count the number of breakpoints in all files
472  int count = 0;
473  for (const auto& fnm_bp_p : bp_list)
474  count += fnm_bp_p.second.size ();
475 
476  Cell names (dim_vector (count, 1));
477  Cell file (dim_vector (count, 1));
478  Cell line (dim_vector (count, 1));
479  Cell cond (dim_vector (count, 1));
480 
481  for (const auto& fnm_bp_p : bp_list)
482  {
483  std::string filename = fnm_bp_p.first;
484  const char *sub_fcn = strchr (filename.c_str (), '>');
485  if (sub_fcn)
486  filename = filename.substr(0, sub_fcn - filename.c_str ());
487  octave_value path_name;
488  path_name
489  = octave::sys::canonicalize_file_name (help_sys.which (filename));
490 
491  for (const auto& bp : fnm_bp_p.second)
492  {
493  names(i) = fnm_bp_p.first;
494  file(i) = path_name;
495  line(i) = octave_value (bp.line);
496  cond(i) = octave_value (bp.cond);
497  i++;
498  }
499  }
500 
501  retmap.assign ("name", names);
502  retmap.assign ("file", file);
503  retmap.assign ("line", line);
504  retmap.assign ("cond", cond);
505 
506  const octave_map ew = bptab.stop_on_err_warn_status (false);
507  if (ew.isempty ())
508  {
509  retval = octave_value (retmap);
510  }
511  else
512  {
513  octave_map outer (dim_vector (3, 1));
514  outer.assign ("bkpt", Cell (retmap));
515  for (auto f = ew.begin (); f != ew.end (); f++)
516  outer.setfield (f->first, ew.contents (f));
517 
518  retval = octave_value (outer);
519  }
520 
521  return retval;
522  }
523 }
524 
525 /*
526 %!test
527 %! if (isguirunning ())
528 %! orig_show_dbg = __event_manager_gui_preference__ ("editor/show_dbg_file",
529 %! "0");
530 %! endif
531 %! unwind_protect
532 %! dbclear all; # Clear out breakpoints before test
533 %! dbstop @ftp/dir;
534 %! dbstop @audioplayer/set 75;
535 %! dbstop quantile>__quantile__;
536 %! dbstop ls;
537 %! s = dbstatus;
538 %! dbclear all;
539 %! assert (s(1).name, "@audioplayer/set>setproperty");
540 %! assert (s(2).name, "@ftp/dir");
541 %! assert (s(3).name, "ls");
542 %! assert (s(4).name, "quantile>__quantile__");
543 %! assert (s(2).file(end-10:end), [filesep "@ftp" filesep "dir.m"]);
544 %! unwind_protect_cleanup
545 %! if (isguirunning ())
546 %! __event_manager_gui_preference__ ("editor/show_dbg_file", orig_show_dbg);
547 %! endif
548 %! end_unwind_protect
549 */
550 
551 DEFMETHOD (dbwhere, interp, , ,
552  doc: /* -*- texinfo -*-
553 @deftypefn {} {} dbwhere
554 In debugging mode, report the current file and line number where execution
555 is stopped.
556 @seealso{dbstack, dblist, dbstatus, dbcont, dbstep, dbup, dbdown}
557 @end deftypefn */)
558 {
559  octave::tree_evaluator& tw = interp.get_evaluator ();
560 
561  tw.debug_where (octave_stdout);
562 
563  return ovl ();
564 }
565 
566 static void
567 do_dbtype (std::ostream& os, const std::string& name, int start, int end)
568 {
569  std::string ff = octave::fcn_file_in_path (name);
570 
571  if (ff.empty ())
572  os << "dbtype: unknown function " << name << "\n";
573  else
574  {
575  std::ifstream fs = octave::sys::ifstream (ff.c_str (), std::ios::in);
576 
577  if (! fs)
578  os << "dbtype: unable to open '" << ff << "' for reading!\n";
579  else
580  {
581  int line = 1;
582  std::string text;
583 
584  while (std::getline (fs, text) && line <= end)
585  {
586  if (line >= start)
587  os << line << "\t" << text << "\n";
588 
589  line++;
590  }
591  }
592  }
593 
594  os.flush ();
595 }
596 
597 DEFMETHOD (dbtype, interp, args, ,
598  doc: /* -*- texinfo -*-
599 @deftypefn {} {} dbtype
600 @deftypefnx {} {} dbtype @var{lineno}
601 @deftypefnx {} {} dbtype @var{startl:endl}
602 @deftypefnx {} {} dbtype @var{startl:end}
603 @deftypefnx {} {} dbtype @var{fcn}
604 @deftypefnx {} {} dbtype @var{fcn} @var{lineno}
605 @deftypefnx {} {} dbtype @var{fcn} @var{startl:endl}
606 @deftypefnx {} {} dbtype @var{fcn} @var{startl:end}
607 Display a script file with line numbers.
608 
609 When called with no arguments in debugging mode, display the script file
610 currently being debugged.
611 
612 An optional range specification can be used to list only a portion of the
613 file. The special keyword @qcode{"end"} is a valid line number
614 specification for the last line of the file.
615 
616 When called with the name of a function, list that script file with line
617 numbers.
618 @seealso{dblist, dbwhere, dbstatus, dbstop}
619 @end deftypefn */)
620 {
621  octave_user_code *dbg_fcn;
622 
623  string_vector argv = args.make_argv ("dbtype");
624 
625  octave::tree_evaluator& tw = interp.get_evaluator ();
626 
627  switch (args.length ())
628  {
629  case 0: // dbtype
630  dbg_fcn = tw.get_user_code ();
631 
632  if (! dbg_fcn)
633  error ("dbtype: must be inside a user function to give no arguments to dbtype\n");
634 
635  do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
637 
638  break;
639 
640  case 1: // (dbtype start:end) || (dbtype fcn) || (dbtype lineno)
641  {
642  std::string arg = argv[1];
643 
644  std::size_t ind = arg.find (':');
645 
646  if (ind != std::string::npos) // (dbtype start:end)
647  {
648  dbg_fcn = tw.get_user_code ();
649 
650  if (dbg_fcn)
651  {
652  std::string start_str = arg.substr (0, ind);
653  std::string end_str = arg.substr (ind + 1);
654 
655  int start, end;
656  start = atoi (start_str.c_str ());
657  if (end_str == "end")
659  else
660  end = atoi (end_str.c_str ());
661 
662  if (std::min (start, end) <= 0)
663  error ("dbtype: start and end lines must be >= 1\n");
664 
665  if (start > end)
666  error ("dbtype: start line must be less than end line\n");
667 
668  do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
669  start, end);
670  }
671  }
672  else // (dbtype fcn) || (dbtype lineno)
673  {
674  int line = atoi (arg.c_str ());
675 
676  if (line == 0) // (dbtype fcn)
677  {
678  dbg_fcn = tw.get_user_code (arg);
679 
680  if (! dbg_fcn)
681  error ("dbtype: function <%s> not found\n", arg.c_str ());
682 
683  do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
685  }
686  else // (dbtype lineno)
687  {
688  if (line <= 0)
689  error ("dbtype: start and end lines must be >= 1\n");
690 
691  dbg_fcn = tw.get_user_code ();
692 
693  if (dbg_fcn)
694  do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
695  line, line);
696  }
697  }
698  }
699  break;
700 
701  case 2: // (dbtype fcn start:end) || (dbtype fcn start)
702  {
703  dbg_fcn = tw.get_user_code (argv[1]);
704 
705  if (! dbg_fcn)
706  error ("dbtype: function <%s> not found\n", argv[1].c_str ());
707 
708  std::string arg = argv[2];
709  int start, end;
710  std::size_t ind = arg.find (':');
711 
712  if (ind != std::string::npos)
713  {
714  std::string start_str = arg.substr (0, ind);
715  std::string end_str = arg.substr (ind + 1);
716 
717  start = atoi (start_str.c_str ());
718  if (end_str == "end")
720  else
721  end = atoi (end_str.c_str ());
722  }
723  else
724  {
725  start = atoi (arg.c_str ());
726  end = start;
727  }
728 
729  if (std::min (start, end) <= 0)
730  error ("dbtype: start and end lines must be >= 1\n");
731 
732  if (start > end)
733  error ("dbtype: start line must be less than end line\n");
734 
735  do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (), start, end);
736  }
737  break;
738 
739  default:
740  error ("dbtype: expecting zero, one, or two arguments\n");
741  }
742 
743  return ovl ();
744 }
745 
746 DEFMETHOD (dblist, interp, args, ,
747  doc: /* -*- texinfo -*-
748 @deftypefn {} {} dblist
749 @deftypefnx {} {} dblist @var{n}
750 In debugging mode, list @var{n} lines of the function being debugged
751 centered around the current line to be executed.
752 
753 If unspecified @var{n} defaults to 10 (+/- 5 lines)
754 @seealso{dbwhere, dbtype, dbstack}
755 @end deftypefn */)
756 {
757  int n = 10;
758 
759  if (args.length () == 1)
760  {
761  octave_value arg = args(0);
762 
763  if (arg.is_string ())
764  {
765  std::string s_arg = arg.string_value ();
766 
767  n = atoi (s_arg.c_str ());
768  }
769  else
770  n = args(0).int_value ();
771 
772  if (n < 0)
773  error ("dblist: N must be a non-negative integer");
774  }
775 
776  octave::tree_evaluator& tw = interp.get_evaluator ();
777 
778  octave_user_code *dbg_fcn = tw.get_user_code ();
779 
780  if (! dbg_fcn)
781  error ("dblist: must be inside a user function to use dblist\n");
782 
783  bool have_file = true;
784 
785  std::string name = dbg_fcn->fcn_file_name ();
786 
787  if (name.empty ())
788  {
789  have_file = false;
790  name = dbg_fcn->name ();
791  }
792 
793  int l = tw.debug_user_code_line ();
794 
795  if (l > 0)
796  {
797  if (have_file)
798  {
799  int l_min = std::max (l - n/2, 0);
800  int l_max = l + n/2;
801  do_dbtype (octave_stdout, name, l_min, l-1);
802 
803  std::string line = dbg_fcn->get_code_line (l);
804 
805  if (! line.empty ())
806  octave_stdout << l << "-->\t" << line << std::endl;
807 
808  do_dbtype (octave_stdout, name, l+1, l_max);
809  }
810  }
811  else
812  {
813  octave_stdout << "dblist: unable to determine source code line"
814  << std::endl;
815  }
816 
817  return ovl ();
818 }
819 
820 static octave_value_list
821 do_dbstack (octave::interpreter& interp, const octave_value_list& args,
822  int nargout, std::ostream& os)
823 {
824  int nargin = args.length ();
825 
826  if (nargin > 2)
827  print_usage ();
828 
829  octave_value_list retval;
830 
831  octave_idx_type curr_frame = -1;
832 
833  octave_idx_type nskip = 0;
834 
835  if (nargin == 1 || nargin == 2)
836  {
837  int n = 0;
838 
839  for (octave_idx_type i = 0; i < nargin; i++)
840  {
841  octave_value arg = args(i);
842 
843  if (arg.is_string ())
844  {
845  std::string s_arg = arg.string_value ();
846 
847  // Skip "-completenames", octave returns full names anyway.
848  if (s_arg == "-completenames")
849  continue;
850 
851  n = atoi (s_arg.c_str ());
852  }
853  else
854  n = arg.int_value ();
855 
856  if (n < 0)
857  error ("dbstack: N must be a non-negative integer");
858  }
859 
860  if (n > 0)
861  nskip = n;
862  }
863 
864  octave::tree_evaluator& tw = interp.get_evaluator ();
865 
866  if (nargout == 0)
867  {
868  octave_map stk = tw.backtrace (curr_frame);
869  octave_idx_type nframes = stk.numel ();
870 
871  if (nframes > 0)
872  {
873  octave::preserve_stream_state stream_state (os);
874 
875  os << "stopped in:\n\n";
876 
877  Cell names = stk.contents ("name");
878  Cell files = stk.contents ("file");
879  Cell lines = stk.contents ("line");
880 
881  bool show_top_level = true;
882 
883  std::size_t max_name_len = 0;
884 
885  for (octave_idx_type i = nskip; i < nframes; i++)
886  {
887  std::string name = names(i).string_value ();
888 
889  max_name_len = std::max (name.length (), max_name_len);
890  }
891 
892  for (octave_idx_type i = nskip; i < nframes; i++)
893  {
894  std::string name = names(i).string_value ();
895  std::string file = files(i).string_value ();
896  int line = lines(i).int_value ();
897 
898  if (show_top_level && i == curr_frame)
899  show_top_level = false;
900 
901  os << (i == curr_frame ? " --> " : " ")
902  << std::setw (max_name_len) << name
903  << " at line " << line
904  << " [" << file << ']'
905  << std::endl;
906  }
907 
908  if (tw.at_top_level () && show_top_level)
909  os << " --> top level" << std::endl;
910  }
911  }
912  else
913  {
914  octave_map stk = tw.backtrace (curr_frame, false);
915 
916  octave_idx_type num_skip = std::min (nskip, stk.numel ());
917 
918  idx_vector first = idx_vector (0);
919 
920  for (octave_idx_type i = 0; i < num_skip; i++)
921  stk.delete_elements (first);
922 
923  curr_frame -= num_skip;
924  curr_frame = (curr_frame < 0 ? 0 : curr_frame + 1);
925 
926  retval = ovl (stk, curr_frame);
927  }
928 
929  return retval;
930 }
931 
932 // A function that can be easily called from a debugger print the Octave stack.
933 // This can be useful for finding what line of code the interpreter is
934 // currently executing when the debugger is stopped in some C++ function,
935 // for example.
936 
937 void
939 {
941  octave_value_list (), 0, std::cerr);
942 }
943 
944 DEFMETHOD (dbstack, interp, args, nargout,
945  doc: /* -*- texinfo -*-
946 @deftypefn {} {} dbstack
947 @deftypefnx {} {} dbstack @var{n}
948 @deftypefnx {} {} dbstack @var{-completenames}
949 @deftypefnx {} {[@var{stack}, @var{idx}] =} dbstack (@dots{})
950 Display or return current debugging function stack information.
951 
952 With optional argument @var{n}, omit the @var{n} innermost stack frames.
953 
954 Although accepted, the argument @var{-completenames} is silently ignored.
955 Octave always returns absolute filenames.
956 
957 The arguments @var{n} and @var{-completenames} can both be specified and may
958 appear in any order.
959 
960 The optional return argument @var{stack} is a struct array with the
961 following fields:
962 
963 @table @asis
964 @item file
965 The name of the m-file where the function code is located.
966 
967 @item name
968 The name of the function with a breakpoint.
969 
970 @item line
971 The line number of an active breakpoint.
972 
973 @item column
974 The column number of the line where the breakpoint begins.
975 
976 @end table
977 
978 The return argument @var{idx} specifies which element of the @var{stack}
979 struct array is currently active.
980 @seealso{dbup, dbdown, dbwhere, dblist, dbstatus}
981 @end deftypefn */)
982 {
983  return do_dbstack (interp, args, nargout, octave_stdout);
984 }
985 
986 static void
987 do_dbupdown (octave::interpreter& interp, const octave_value_list& args,
988  const std::string& who)
989 {
990  int n = 1;
991 
992  if (args.length () == 1)
993  {
994  octave_value arg = args(0);
995 
996  if (arg.is_string ())
997  {
998  std::string s_arg = arg.string_value ();
999 
1000  n = atoi (s_arg.c_str ());
1001  }
1002  else
1003  n = args(0).int_value ();
1004  }
1005 
1006  if (who == "dbup")
1007  n = -n;
1008 
1009  octave::tree_evaluator& tw = interp.get_evaluator ();
1010 
1011  tw.dbupdown (n, true);
1012 }
1013 
1014 DEFMETHOD (dbup, interp, args, ,
1015  doc: /* -*- texinfo -*-
1016 @deftypefn {} {} dbup
1017 @deftypefnx {} {} dbup @var{n}
1018 In debugging mode, move up the execution stack @var{n} frames.
1019 
1020 If @var{n} is omitted, move up one frame.
1021 @seealso{dbstack, dbdown}
1022 @end deftypefn */)
1023 {
1024  do_dbupdown (interp, args, "dbup");
1025 
1026  return ovl ();
1027 }
1028 
1029 DEFMETHOD (dbdown, interp, args, ,
1030  doc: /* -*- texinfo -*-
1031 @deftypefn {} {} dbdown
1032 @deftypefnx {} {} dbdown @var{n}
1033 In debugging mode, move down the execution stack @var{n} frames.
1034 
1035 If @var{n} is omitted, move down one frame.
1036 @seealso{dbstack, dbup}
1037 @end deftypefn */)
1038 {
1039  do_dbupdown (interp, args, "dbdown");
1040 
1041  return ovl ();
1042 }
1043 
1044 DEFMETHOD (dbstep, interp, args, ,
1045  doc: /* -*- texinfo -*-
1046 @deftypefn {} {} dbstep
1047 @deftypefnx {} {} dbstep @var{n}
1048 @deftypefnx {} {} dbstep in
1049 @deftypefnx {} {} dbstep out
1050 @deftypefnx {} {} dbnext @dots{}
1051 In debugging mode, execute the next @var{n} lines of code.
1052 
1053 If @var{n} is omitted, execute the next single line of code. If the next
1054 line of code is itself defined in terms of an m-file remain in the existing
1055 function.
1056 
1057 Using @code{dbstep in} will cause execution of the next line to step into
1058 any m-files defined on the next line.
1059 
1060 Using @code{dbstep out} will cause execution to continue until the current
1061 function returns.
1062 
1063 @code{dbnext} is an alias for @code{dbstep}.
1064 @seealso{dbcont, dbquit}
1065 @end deftypefn */)
1066 {
1067  octave::tree_evaluator& tw = interp.get_evaluator ();
1068 
1069  if (! tw.in_debug_repl ())
1070  error ("dbstep: can only be called in debug mode");
1071 
1072  int nargin = args.length ();
1073 
1074  if (nargin > 1)
1075  print_usage ();
1076 
1077  int n = 0;
1078 
1079  if (nargin == 1)
1080  {
1081  std::string arg
1082  = args(0).xstring_value ("dbstep: input argument must be a string");
1083 
1084  if (arg == "in")
1085  n = -1;
1086  else if (arg == "out")
1087  n = -2;
1088  else
1089  {
1090  n = atoi (arg.c_str ());
1091 
1092  if (n < 1)
1093  error ("dbstep: invalid argument");
1094  }
1095  }
1096  else
1097  n = 1;
1098 
1099  if (n != 0)
1100  {
1101  tw.set_dbstep_flag (n);
1102 
1103  // If we set the dbstep flag, we also need to reset debug_mode.
1104  tw.reset_debug_state ();
1105 
1106  }
1107 
1108  return ovl ();
1109 }
1110 
1111 DEFALIAS (dbnext, dbstep);
1112 
1113 DEFMETHOD (dbcont, interp, args, ,
1114  doc: /* -*- texinfo -*-
1115 @deftypefn {} {} dbcont
1116 Leave command-line debugging mode and continue code execution normally.
1117 @seealso{dbstep, dbquit}
1118 @end deftypefn */)
1119 {
1120  octave::tree_evaluator& tw = interp.get_evaluator ();
1121 
1122  if (! tw.in_debug_repl ())
1123  error ("dbcont: can only be called in debug mode");
1124 
1125  if (args.length () != 0)
1126  print_usage ();
1127 
1128  tw.dbcont ();
1129 
1130  return ovl ();
1131 }
1132 
1133 DEFMETHOD (dbquit, interp, args, ,
1134  doc: /* -*- texinfo -*-
1135 @deftypefn {} {} dbquit
1136 @deftypefnx {} {} dbquit all
1137 Quit debugging mode immediately without further code execution. With no
1138 arguments, exit the current debugging level. With argument @code{all},
1139 exit all debugging levels and return to the Octave prompt.
1140 @seealso{dbcont, dbstep}
1141 @end deftypefn */)
1142 {
1143  octave::tree_evaluator& tw = interp.get_evaluator ();
1144 
1145  if (! tw.in_debug_repl ())
1146  error ("dbquit: can only be called in debug mode");
1147 
1148  int nargin = args.length ();
1149 
1150  if (nargin > 1)
1151  print_usage ();
1152 
1153  if (nargin == 1)
1154  {
1155  std::string arg
1156  = args(0).xstring_value ("dbquit: input argument must be a string");
1157 
1158  if (arg == "all")
1159  tw.dbquit (true);
1160  else
1161  error ("dbquit: unrecognized argument '%s'", arg.c_str ());
1162  }
1163  else
1164  tw.dbquit ();
1165 
1166  return ovl ();
1167 }
1168 
1169 DEFMETHOD (isdebugmode, interp, args, ,
1170  doc: /* -*- texinfo -*-
1171 @deftypefn {} {@var{tf} =} isdebugmode ()
1172 Return true if in debugging mode, otherwise false.
1173 @seealso{dbwhere, dbstack, dbstatus}
1174 @end deftypefn */)
1175 {
1176  if (args.length () != 0)
1177  print_usage ();
1178 
1179  octave::tree_evaluator& tw = interp.get_evaluator ();
1180 
1181  return ovl (tw.in_debug_repl ());
1182 }
1183 
1184 DEFMETHOD (__db_next_breakpoint_quiet__, interp, args, ,
1185  doc: /* -*- texinfo -*-
1186 @deftypefn {} {} __db_next_breakpoint_quiet__ ()
1187 @deftypefnx {} {} __db_next_breakpoint_quiet__ (@var{flag})
1188 Disable line info printing at the next breakpoint.
1189 
1190 With a logical argument @var{flag}, set the state on or off.
1191 @end deftypefn */)
1192 {
1193  int nargin = args.length ();
1194 
1195  if (nargin > 1)
1196  print_usage ();
1197 
1198  bool state = true;
1199 
1200  if (nargin == 1)
1201  state = args(0).bool_value ();
1202 
1203  octave::tree_evaluator& tw = interp.get_evaluator ();
1204 
1205  tw.quiet_breakpoint_flag (state);
1206 
1207  return ovl ();
1208 }
1209 
OCTAVE_END_NAMESPACE(octave)
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:207
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:414
OCTARRAY_API void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array-base.cc:1032
Definition: Cell.h:43
Cell & insert(const Cell &a, octave_idx_type r, octave_idx_type c)
Definition: Cell.cc:330
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
std::string name(void) const
Definition: ov-fcn.h:208
Cell getfield(const std::string &key) const
Definition: oct-map.cc:275
bool isfield(const std::string &name) const
Definition: oct-map.h:347
void delete_elements(const octave::idx_vector &i)
Definition: oct-map.cc:1228
void setfield(const std::string &key, const Cell &val)
Definition: oct-map.cc:282
octave_idx_type numel(void) const
Definition: oct-map.h:389
void assign(const std::string &k, const Cell &val)
Definition: oct-map.h:365
const_iterator begin(void) const
Definition: oct-map.h:318
const_iterator end(void) const
Definition: oct-map.h:319
const Cell & contents(const_iterator p) const
Definition: oct-map.h:331
bool isempty(void) const
Definition: oct-map.h:391
std::string fcn_file_name(void) const
Definition: ov-usr-fcn.h:107
std::string get_code_line(std::size_t line)
Definition: ov-usr-fcn.cc:100
octave_idx_type length(void) const
Definition: ovl.h:113
bool iscell(void) const
Definition: ov.h:649
int int_value(bool req_int=false, bool frc_str_conv=false) const
Definition: ov.h:857
bool is_string(void) const
Definition: ov.h:682
Cell cell_value(void) const
Matrix size(void)
Definition: ov.h:511
std::string string_value(bool force=false) const
Definition: ov.h:1019
bool isempty(void) const
Definition: ov.h:646
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
static octave_value_list do_dbstack(octave::interpreter &interp, const octave_value_list &args, int nargout, std::ostream &os)
Definition: debug.cc:821
void show_octave_dbstack(void)
Definition: debug.cc:938
static void do_dbtype(std::ostream &os, const std::string &name, int start, int end)
Definition: debug.cc:567
static void do_dbupdown(octave::interpreter &interp, const octave_value_list &args, const std::string &who)
Definition: debug.cc:987
static octave_value bp_lines_to_ov(const octave::bp_table::bp_lines &lines)
Definition: debug.cc:65
OCTINTERP_API void print_usage(void)
Definition: defun-int.h:72
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition: defun.h:111
#define DEFALIAS(alias, name)
Macro to define an alias for another existing function name.
Definition: defun.h:160
void error(const char *fmt,...)
Definition: error.cc:979
void err_wrong_type_arg(const char *name, const char *s)
Definition: errwarn.cc:166
std::string canonicalize_file_name(const std::string &name)
Definition: file-ops.cc:744
octave::idx_vector idx_vector
Definition: idx-vector.h:1039
interpreter & __get_interpreter__(void)
F77_RET_T const F77_DBLE const F77_DBLE * f
std::ifstream ifstream(const std::string &filename, const std::ios::openmode mode)
Definition: lo-sysdep.cc:424
T octave_idx_type m
Definition: mx-inlines.cc:773
octave_idx_type n
Definition: mx-inlines.cc:753
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
#define octave_stdout
Definition: pager.h:314
static uint32_t state[624]
Definition: randmtzig.cc:193
std::string fcn_file_in_path(const std::string &name)
Definition: utils.cc:742