GNU Octave  9.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-2024 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  std::string fcn_ident;
193  if (class_name.empty ())
194  fcn_ident = symbol_name;
195  else
196  fcn_ident = "@" + class_name + "/" + symbol_name;
197 
198  retmap = bptab.add_breakpoints_in_function (fcn_ident, lines,
199  condition);
200  retval = bp_lines_to_ov (retmap);
201  }
202  }
203  else if (args.length () != 1)
204  {
205  print_usage ();
206  }
207  else // structure of the form output by dbstatus
208  {
209  octave_map mv = args(0).map_value ();
210  if (mv.isfield ("bkpt") || mv.isfield ("errs") || mv.isfield ("warn")
211  || mv.isfield ("intr"))
212  {
213  bptab.dbstop_process_map_args (mv);
214 
215  // Replace mv by "bkpt", to use the processing below.
216  octave_value bkpt = mv.getfield ("bkpt");
217  if (bkpt.isempty ())
218  mv = octave_map ();
219  else
220  {
221  if (bkpt.iscell () && bkpt.cell_value ().numel () > 0
222  && bkpt.cell_value () (0).isstruct ())
223  mv = bkpt.cell_value () (0).map_value ();
224  else
225  error ("dbstop: invalid 'bkpt' field");
226  }
227  }
228  if (mv.isempty ())
229  {
230  // no changes requested. Occurs if "errs" non-empty but "bkpt" empty
231  }
232  else if (! mv.isfield ("name") || ! mv.isfield ("line"))
233  {
234  error ("dbstop: Cell array must contain fields 'name' and 'line'");
235  }
236  else
237  {
238  bool use_cond = mv.isfield ("cond");
239  Cell name = mv.getfield ("name");
240  Cell line = mv.getfield ("line");
241  Cell cond = (use_cond ? mv.getfield ("cond") : Cell ());
242  std::string unconditional = "";
243  for (octave_idx_type i = 0; i < line.numel (); i++)
244  {
245  // FIXME: This isn't very clear, but add_breakpoints_in_function
246  // requires this argument to be of type bp_table::bp_lines type
247  // which is std::set<int>.
248  lines.clear ();
249  lines.insert (line(i).int_value ());
250  bptab.add_breakpoints_in_function (name(i).string_value (),
251  lines,
252  (use_cond
253  ? cond(i).string_value ()
254  : unconditional));
255  }
256  retval = octave_value (line.numel ());
257  }
258  }
259 
260  // If we add a breakpoint, we also need to reset debug_mode.
261  tw.reset_debug_state ();
262 
263  return retval;
264 }
265 
266 DEFMETHOD (dbclear, interp, args, ,
267  doc: /* -*- texinfo -*-
268 @deftypefn {} {} dbclear @var{fcn}
269 @deftypefnx {} {} dbclear @var{fcn} @var{line}
270 @deftypefnx {} {} dbclear @var{fcn} @var{line1} @var{line2} @dots{}
271 @deftypefnx {} {} dbclear @var{line} @dots{}
272 @deftypefnx {} {} dbclear all
273 @deftypefnx {} {} dbclear in @var{fcn}
274 @deftypefnx {} {} dbclear in @var{fcn} at @var{line}
275 @deftypefnx {} {} dbclear if @var{event}
276 @deftypefnx {} {} dbclear ("@var{fcn}")
277 @deftypefnx {} {} dbclear ("@var{fcn}", @var{line})
278 @deftypefnx {} {} dbclear ("@var{fcn}", @var{line1}, @var{line2}, @dots{})
279 @deftypefnx {} {} dbclear ("@var{fcn}", @var{line1}, @dots{})
280 @deftypefnx {} {} dbclear (@var{line}, @dots{})
281 @deftypefnx {} {} dbclear ("all")
282 Delete a breakpoint at line number @var{line} in the function @var{fcn}.
283 
284 Arguments are
285 
286 @table @var
287 @item fcn
288 Function name as a string variable. When already in debug mode this
289 argument can be omitted and the current function will be used.
290 
291 @item line
292 Line number from which to remove a breakpoint. Multiple lines may be given
293 as separate arguments or as a vector.
294 
295 @item event
296 An event such as @code{error}, @code{interrupt}, or @code{warning}
297 (@pxref{XREFdbstop,,@code{dbstop}} for details).
298 @end table
299 
300 When called without a line number specification all breakpoints in the named
301 function are cleared.
302 
303 If the requested line is not a breakpoint no action is performed.
304 
305 The special keyword @qcode{"all"} will clear all breakpoints from all
306 files.
307 @seealso{dbstop, dbstatus, dbwhere}
308 @end deftypefn */)
309 {
310  std::string symbol_name = ""; // stays empty for "dbclear if error" etc
311  std::string class_name = "";
312  octave::bp_table::bp_lines lines;
313  std::string dummy; // "if" condition -- only used for dbstop
314 
315  octave::tree_evaluator& tw = interp.get_evaluator ();
316 
317  octave::bp_table& bptab = tw.get_bp_table ();
318 
319  bptab.parse_dbfunction_params ("dbclear", args, symbol_name, class_name,
320  lines, dummy);
321 
322  if (args.length () == 1 && symbol_name == "all")
323  {
324  bptab.remove_all_breakpoints ();
325  bptab.dbclear_all_signals ();
326  }
327  else if (symbol_name != "")
328  {
329  std::string fcn_ident;
330  if (class_name.empty ())
331  fcn_ident = symbol_name;
332  else
333  fcn_ident = "@" + class_name + "/" + symbol_name;
334 
335  bptab.remove_breakpoints_from_function (fcn_ident, lines);
336  }
337 
338  // If we remove a breakpoint, we also need to reset debug_mode.
339  tw.reset_debug_state ();
340 
341  return ovl ();
342 }
343 
344 DEFMETHOD (dbstatus, interp, args, nargout,
345  doc: /* -*- texinfo -*-
346 @deftypefn {} {} dbstatus
347 @deftypefnx {} {} dbstatus @var{fcn}
348 @deftypefnx {} {@var{bp_list} =} dbstatus @dots{}
349 Report the location of active breakpoints.
350 
351 When called with no input or output arguments, print the list of all
352 functions with breakpoints and the line numbers where those breakpoints are
353 set.
354 
355 If a function name @var{fcn} is specified then only report breakpoints
356 for the named function and its subfunctions.
357 
358 The optional return argument @var{bp_list} is a struct array with the
359 following fields:
360 
361 @table @asis
362 @item name
363 The name of the function with a breakpoint. A subfunction, say @code{fcn2}
364 within an m-file, say @file{file.m}, is specified as @code{file>fcn2}.
365 
366 @item file
367 The name of the m-file where the function code is located.
368 
369 @item line
370 The line number with the breakpoint.
371 
372 @item cond
373 The condition that must be satisfied for the breakpoint to be active, or
374 the empty string for unconditional breakpoints.
375 @end table
376 
377 @c Note: When @code{dbstatus} is called from the debug prompt within a function,
378 @c the list of breakpoints is automatically trimmed to the breakpoints in the
379 @c current function.
380 If @code{dbstop if error} is true but no explicit IDs are specified, the
381 return value will have an empty field called @qcode{"errs"}. If IDs are
382 specified, the @code{errs} field will have one row per ID@. If
383 @code{dbstop if error} is false, there is no @qcode{"errs"} field.
384 The @qcode{"warn"} field is set similarly by @code{dbstop if warning}.
385 
386 @seealso{dbstop, dbclear, dbwhere, dblist, dbstack}
387 @end deftypefn */)
388 {
389  int nargin = args.length ();
390 
391  if (nargin != 0 && nargin != 1)
392  error ("dbstatus: only zero or one arguments accepted\n");
393 
394  octave_value_list fcn_list;
395  octave::bp_table::fname_bp_map bp_list;
396  std::string symbol_name;
397 
398  octave::tree_evaluator& tw = interp.get_evaluator ();
399 
400  octave::bp_table& bptab = tw.get_bp_table ();
401 
402  if (nargin == 1)
403  {
404  if (! args(0).is_string ())
405  err_wrong_type_arg ("dbstatus", args(0));
406 
407  symbol_name = args(0).string_value ();
408  fcn_list(0) = symbol_name;
409  bp_list = bptab.get_breakpoint_list (fcn_list);
410  }
411  else
412  {
413  /*
414  if (tw.in_debug_repl ())
415  {
416  octave_user_code *dbg_fcn = tw.get_user_code ();
417  if (dbg_fcn)
418  {
419  symbol_name = dbg_fcn->name ();
420  fcn_list(0) = symbol_name;
421  }
422  }
423  */
424 
425  bp_list = bptab.get_breakpoint_list (fcn_list);
426  }
427 
428  if (nargout == 0)
429  {
430  // Print out the breakpoint information.
431 
432  for (auto& fnm_bp_p: bp_list)
433  {
434  std::list<octave::bp_type> m = fnm_bp_p.second;
435 
436  // print unconditional breakpoints, if any, on a single line
437 
438  // first, check to see if there are any
439  int have_unconditional = 0;
440  for (const auto& bp : m)
441  {
442  if (bp.cond == "")
443  {
444  if (have_unconditional++)
445  break; // stop once we know its plural
446  }
447  }
448  // If we actually have some, print line numbers only
449  if (have_unconditional)
450  {
451  const char *_s_ = (have_unconditional > 1) ? "s" : "";
452  octave_stdout << "breakpoint" << _s_ << " in " << fnm_bp_p.first
453  << " at line" << _s_ << ' ';
454 
455  for (const auto& bp : m)
456  {
457  if (bp.cond == "")
458  octave_stdout << bp.line << ' ';
459  }
460  octave_stdout << std::endl;
461  }
462 
463  // print conditional breakpoints, one per line, with conditions
464  for (const auto& bp : m)
465  {
466  if (bp.cond != "")
467  octave_stdout << "breakpoint in " << fnm_bp_p.first
468  << " at line " << bp.line
469  << " if " << bp.cond << "\n";
470  }
471  }
472 
473  bptab.stop_on_err_warn_status (true);
474 
475  return ovl ();
476  }
477  else
478  {
479  octave::help_system& help_sys = interp.get_help_system ();
480 
481  // Fill in an array for return.
482  int i = 0;
483  octave_map retmap;
484  octave_value retval;
485 
486  // count the number of breakpoints in all files
487  int count = 0;
488  for (const auto& fnm_bp_p : bp_list)
489  count += fnm_bp_p.second.size ();
490 
491  Cell names (dim_vector (count, 1));
492  Cell file (dim_vector (count, 1));
493  Cell line (dim_vector (count, 1));
494  Cell cond (dim_vector (count, 1));
495 
496  for (const auto& fnm_bp_p : bp_list)
497  {
498  std::string filename = fnm_bp_p.first;
499  const char *sub_fcn = strchr (filename.c_str (), '>');
500  if (sub_fcn)
501  filename = filename.substr(0, sub_fcn - filename.c_str ());
502  octave_value path_name;
503  path_name
504  = octave::sys::canonicalize_file_name (help_sys.which (filename));
505 
506  for (const auto& bp : fnm_bp_p.second)
507  {
508  names(i) = fnm_bp_p.first;
509  file(i) = path_name;
510  line(i) = octave_value (bp.line);
511  cond(i) = octave_value (bp.cond);
512  i++;
513  }
514  }
515 
516  retmap.assign ("name", names);
517  retmap.assign ("file", file);
518  retmap.assign ("line", line);
519  retmap.assign ("cond", cond);
520 
521  const octave_map ew = bptab.stop_on_err_warn_status (false);
522  if (ew.isempty ())
523  {
524  retval = octave_value (retmap);
525  }
526  else
527  {
528  octave_map outer (dim_vector (3, 1));
529  outer.assign ("bkpt", Cell (retmap));
530  for (auto f = ew.begin (); f != ew.end (); f++)
531  outer.setfield (f->first, ew.contents (f));
532 
533  retval = octave_value (outer);
534  }
535 
536  return retval;
537  }
538 }
539 
540 /*
541 %!test
542 %! if (isguirunning ())
543 %! orig_show_dbg = __event_manager_gui_preference__ ("editor/show_dbg_file",
544 %! "false");
545 %! endif
546 %! unwind_protect
547 %! dbclear all; # Clear out breakpoints before test
548 %! dbstop @ftp/dir;
549 %! dbstop @audioplayer/set 75;
550 %! dbstop quantile>__quantile__;
551 %! dbstop ls;
552 %! dbstop in inputParser at addOptional;
553 %! dbstop in inputParser at 285;
554 %! s = dbstatus ();
555 %! dbclear all;
556 %! ## For Matlab compatibility, the following name should be:
557 %! ## audioplayer.set>setproperty
558 %! assert (s(1).name, "@audioplayer/set>setproperty");
559 %! ## For Matlab compatibility, the following name should be:
560 %! ## ftp.dir
561 %! assert (s(2).name, "@ftp/dir");
562 %! assert (s(2).file(end-10:end), [filesep "@ftp" filesep "dir.m"]);
563 %! ## For Matlab compatibility, the following two names should be:
564 %! ## inputParser.inputParser>inputParser.addOptional
565 %! assert (s(3).name, "@inputParser/addOptional");
566 %! assert (s(3).line, 278);
567 %! assert (s(4).name, "@inputParser/addOptional");
568 %! assert (s(4).line, 285);
569 %! assert (s(5).name, "ls");
570 %! assert (s(6).name, "quantile>__quantile__");
571 %! s = dbstatus ();
572 %! assert (isempty (s));
573 %! unwind_protect_cleanup
574 %! if (isguirunning ())
575 %! __event_manager_gui_preference__ ("editor/show_dbg_file", orig_show_dbg);
576 %! endif
577 %! end_unwind_protect
578 */
579 
580 DEFMETHOD (dbwhere, interp, , ,
581  doc: /* -*- texinfo -*-
582 @deftypefn {} {} dbwhere
583 In debugging mode, report the current file and line number where execution
584 is stopped.
585 @seealso{dbstack, dblist, dbstatus, dbcont, dbstep, dbup, dbdown}
586 @end deftypefn */)
587 {
588  octave::tree_evaluator& tw = interp.get_evaluator ();
589 
590  tw.debug_where (octave_stdout);
591 
592  return ovl ();
593 }
594 
595 static void
596 do_dbtype (std::ostream& os, const std::string& name, int start, int end)
597 {
598  std::string ff = octave::fcn_file_in_path (name);
599 
600  if (ff.empty ())
601  os << "dbtype: unknown function " << name << "\n";
602  else
603  {
604  std::ifstream fs = octave::sys::ifstream (ff.c_str (), std::ios::in);
605 
606  if (! fs)
607  os << "dbtype: unable to open '" << ff << "' for reading!\n";
608  else
609  {
610  int line = 1;
611  std::string text;
612 
613  while (std::getline (fs, text) && line <= end)
614  {
615  if (line >= start)
616  os << line << "\t" << text << "\n";
617 
618  line++;
619  }
620  }
621  }
622 
623  os.flush ();
624 }
625 
626 DEFMETHOD (dbtype, interp, args, ,
627  doc: /* -*- texinfo -*-
628 @deftypefn {} {} dbtype
629 @deftypefnx {} {} dbtype @var{lineno}
630 @deftypefnx {} {} dbtype @var{startl:endl}
631 @deftypefnx {} {} dbtype @var{startl}:end
632 @deftypefnx {} {} dbtype @var{fcn}
633 @deftypefnx {} {} dbtype @var{fcn} @var{lineno}
634 @deftypefnx {} {} dbtype @var{fcn} @var{startl:endl}
635 @deftypefnx {} {} dbtype @var{fcn} @var{startl}:end
636 Display a script file with line numbers.
637 
638 When called with no arguments in debugging mode, display the script file
639 currently being debugged.
640 
641 An optional range specification can be used to list only a portion of the
642 file. The special keyword @qcode{"end"} is a valid line number specification
643 for the last line of the file.
644 
645 When called with the name of a function, list that script file with line
646 numbers.
647 @seealso{dblist, dbwhere, dbstatus, dbstop}
648 @end deftypefn */)
649 {
650  octave_user_code *dbg_fcn;
651 
652  string_vector argv = args.make_argv ("dbtype");
653 
654  octave::tree_evaluator& tw = interp.get_evaluator ();
655 
656  switch (args.length ())
657  {
658  case 0: // dbtype
659  dbg_fcn = tw.get_user_code ();
660 
661  if (! dbg_fcn)
662  error ("dbtype: must be inside a user function to give no arguments to dbtype\n");
663 
664  do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
666 
667  break;
668 
669  case 1: // (dbtype start:end) || (dbtype fcn) || (dbtype lineno)
670  {
671  std::string arg = argv[1];
672 
673  std::size_t ind = arg.find (':');
674 
675  if (ind != std::string::npos) // (dbtype start:end)
676  {
677  dbg_fcn = tw.get_user_code ();
678 
679  if (dbg_fcn)
680  {
681  std::string start_str = arg.substr (0, ind);
682  std::string end_str = arg.substr (ind + 1);
683 
684  int start, end;
685  start = atoi (start_str.c_str ());
686  if (end_str == "end")
688  else
689  end = atoi (end_str.c_str ());
690 
691  if (std::min (start, end) <= 0)
692  error ("dbtype: start and end lines must be >= 1\n");
693 
694  if (start > end)
695  error ("dbtype: start line must be less than end line\n");
696 
697  do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
698  start, end);
699  }
700  }
701  else // (dbtype fcn) || (dbtype lineno)
702  {
703  int line = atoi (arg.c_str ());
704 
705  if (line == 0) // (dbtype fcn)
706  {
707  dbg_fcn = tw.get_user_code (arg);
708 
709  if (! dbg_fcn)
710  error ("dbtype: function <%s> not found\n", arg.c_str ());
711 
712  do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
714  }
715  else // (dbtype lineno)
716  {
717  if (line <= 0)
718  error ("dbtype: start and end lines must be >= 1\n");
719 
720  dbg_fcn = tw.get_user_code ();
721 
722  if (dbg_fcn)
723  do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
724  line, line);
725  }
726  }
727  }
728  break;
729 
730  case 2: // (dbtype fcn start:end) || (dbtype fcn start)
731  {
732  dbg_fcn = tw.get_user_code (argv[1]);
733 
734  if (! dbg_fcn)
735  error ("dbtype: function <%s> not found\n", argv[1].c_str ());
736 
737  std::string arg = argv[2];
738  int start, end;
739  std::size_t ind = arg.find (':');
740 
741  if (ind != std::string::npos)
742  {
743  std::string start_str = arg.substr (0, ind);
744  std::string end_str = arg.substr (ind + 1);
745 
746  start = atoi (start_str.c_str ());
747  if (end_str == "end")
749  else
750  end = atoi (end_str.c_str ());
751  }
752  else
753  {
754  start = atoi (arg.c_str ());
755  end = start;
756  }
757 
758  if (std::min (start, end) <= 0)
759  error ("dbtype: start and end lines must be >= 1\n");
760 
761  if (start > end)
762  error ("dbtype: start line must be less than end line\n");
763 
764  do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (), start, end);
765  }
766  break;
767 
768  default:
769  error ("dbtype: expecting zero, one, or two arguments\n");
770  }
771 
772  return ovl ();
773 }
774 
775 DEFMETHOD (dblist, interp, args, ,
776  doc: /* -*- texinfo -*-
777 @deftypefn {} {} dblist
778 @deftypefnx {} {} dblist @var{n}
779 In debugging mode, list @var{n} lines of the function being debugged
780 centered around the current line to be executed.
781 
782 If unspecified @var{n} defaults to 10 (+/- 5 lines)
783 @seealso{dbwhere, dbtype, dbstack}
784 @end deftypefn */)
785 {
786  int n = 10;
787 
788  if (args.length () == 1)
789  {
790  octave_value arg = args(0);
791 
792  if (arg.is_string ())
793  {
794  std::string s_arg = arg.string_value ();
795 
796  n = atoi (s_arg.c_str ());
797  }
798  else
799  n = args(0).int_value ();
800 
801  if (n < 0)
802  error ("dblist: N must be a non-negative integer");
803  }
804 
805  octave::tree_evaluator& tw = interp.get_evaluator ();
806 
807  octave_user_code *dbg_fcn = tw.get_user_code ();
808 
809  if (! dbg_fcn)
810  error ("dblist: must be inside a user function to use dblist\n");
811 
812  bool have_file = true;
813 
814  std::string name = dbg_fcn->fcn_file_name ();
815 
816  if (name.empty ())
817  {
818  have_file = false;
819  name = dbg_fcn->name ();
820  }
821 
822  int l = tw.debug_user_code_line ();
823 
824  if (l > 0)
825  {
826  if (have_file)
827  {
828  int l_min = std::max (l - n/2, 0);
829  int l_max = l + n/2;
830  do_dbtype (octave_stdout, name, l_min, l-1);
831 
832  std::string line = dbg_fcn->get_code_line (l);
833 
834  if (! line.empty ())
835  octave_stdout << l << "-->\t" << line << std::endl;
836 
837  do_dbtype (octave_stdout, name, l+1, l_max);
838  }
839  }
840  else
841  {
842  octave_stdout << "dblist: unable to determine source code line"
843  << std::endl;
844  }
845 
846  return ovl ();
847 }
848 
849 static octave_value_list
850 do_dbstack (octave::interpreter& interp, const octave_value_list& args,
851  int nargout, std::ostream& os)
852 {
853  int nargin = args.length ();
854 
855  if (nargin > 2)
856  print_usage ();
857 
858  octave_value_list retval;
859 
860  octave_idx_type curr_frame = -1;
861 
862  octave_idx_type nskip = 0;
863 
864  if (nargin == 1 || nargin == 2)
865  {
866  int n = 0;
867 
868  for (octave_idx_type i = 0; i < nargin; i++)
869  {
870  octave_value arg = args(i);
871 
872  if (arg.is_string ())
873  {
874  std::string s_arg = arg.string_value ();
875 
876  // Skip "-completenames", octave returns full names anyway.
877  if (s_arg == "-completenames")
878  continue;
879 
880  n = atoi (s_arg.c_str ());
881  }
882  else
883  n = arg.int_value ();
884 
885  if (n < 0)
886  error ("dbstack: N must be a non-negative integer");
887  }
888 
889  if (n > 0)
890  nskip = n;
891  }
892 
893  octave::tree_evaluator& tw = interp.get_evaluator ();
894 
895  if (nargout == 0)
896  {
897  octave_map stk = tw.backtrace (curr_frame);
898  octave_idx_type nframes = stk.numel ();
899 
900  if (nframes > 0)
901  {
902  octave::preserve_stream_state stream_state (os);
903 
904  os << "stopped in:\n\n";
905 
906  Cell names = stk.contents ("name");
907  Cell files = stk.contents ("file");
908  Cell lines = stk.contents ("line");
909 
910  bool show_top_level = true;
911 
912  std::size_t max_name_len = 0;
913 
914  for (octave_idx_type i = nskip; i < nframes; i++)
915  {
916  std::string name = names(i).string_value ();
917 
918  max_name_len = std::max (name.length (), max_name_len);
919  }
920 
921  for (octave_idx_type i = nskip; i < nframes; i++)
922  {
923  std::string name = names(i).string_value ();
924  std::string file = files(i).string_value ();
925  int line = lines(i).int_value ();
926 
927  if (show_top_level && i == curr_frame)
928  show_top_level = false;
929 
930  os << (i == curr_frame ? " --> " : " ")
931  << std::setw (max_name_len) << name
932  << " at line " << line
933  << " [" << file << ']'
934  << std::endl;
935  }
936 
937  if (tw.at_top_level () && show_top_level)
938  os << " --> top level" << std::endl;
939  }
940  }
941  else
942  {
943  octave_map stk = tw.backtrace (curr_frame, false);
944 
945  octave_idx_type num_skip = std::min (nskip, stk.numel ());
946 
947  idx_vector first = idx_vector (0);
948 
949  for (octave_idx_type i = 0; i < num_skip; i++)
950  stk.delete_elements (first);
951 
952  curr_frame -= num_skip;
953  curr_frame = (curr_frame < 0 ? 0 : curr_frame + 1);
954 
955  retval = ovl (stk, curr_frame);
956  }
957 
958  return retval;
959 }
960 
961 // A function that can be easily called from a debugger print the Octave stack.
962 // This can be useful for finding what line of code the interpreter is
963 // currently executing when the debugger is stopped in some C++ function,
964 // for example.
965 
966 void
968 {
969  do_dbstack (octave::__get_interpreter__ (),
970  octave_value_list (), 0, std::cerr);
971 }
972 
973 DEFMETHOD (dbstack, interp, args, nargout,
974  doc: /* -*- texinfo -*-
975 @deftypefn {} {} dbstack
976 @deftypefnx {} {} dbstack @var{n}
977 @deftypefnx {} {} dbstack @var{-completenames}
978 @deftypefnx {} {[@var{stack}, @var{idx}] =} dbstack (@dots{})
979 Display or return current debugging function stack information.
980 
981 With optional argument @var{n}, omit the @var{n} innermost stack frames.
982 
983 Although accepted, the argument @var{-completenames} is silently ignored.
984 Octave always returns absolute filenames.
985 
986 The arguments @var{n} and @var{-completenames} can both be specified and may
987 appear in any order.
988 
989 The optional return argument @var{stack} is a struct array with the
990 following fields:
991 
992 @table @asis
993 @item file
994 The name of the m-file where the function code is located.
995 
996 @item name
997 The name of the function with a breakpoint.
998 
999 @item line
1000 The line number of an active breakpoint.
1001 
1002 @item column
1003 The column number of the line where the breakpoint begins.
1004 
1005 @end table
1006 
1007 The return argument @var{idx} specifies which element of the @var{stack}
1008 struct array is currently active.
1009 @seealso{dbup, dbdown, dbwhere, dblist, dbstatus}
1010 @end deftypefn */)
1011 {
1012  return do_dbstack (interp, args, nargout, octave_stdout);
1013 }
1014 
1015 static void
1016 do_dbupdown (octave::interpreter& interp, const octave_value_list& args,
1017  const std::string& who)
1018 {
1019  int n = 1;
1020 
1021  if (args.length () == 1)
1022  {
1023  octave_value arg = args(0);
1024 
1025  if (arg.is_string ())
1026  {
1027  std::string s_arg = arg.string_value ();
1028 
1029  n = atoi (s_arg.c_str ());
1030  }
1031  else
1032  n = args(0).int_value ();
1033  }
1034 
1035  if (who == "dbup")
1036  n = -n;
1037 
1038  octave::tree_evaluator& tw = interp.get_evaluator ();
1039 
1040  tw.dbupdown (n, true);
1041 }
1042 
1043 DEFMETHOD (dbup, interp, args, ,
1044  doc: /* -*- texinfo -*-
1045 @deftypefn {} {} dbup
1046 @deftypefnx {} {} dbup @var{n}
1047 In debugging mode, move up the execution stack @var{n} frames.
1048 
1049 If @var{n} is omitted, move up one frame.
1050 @seealso{dbstack, dbdown}
1051 @end deftypefn */)
1052 {
1053  do_dbupdown (interp, args, "dbup");
1054 
1055  return ovl ();
1056 }
1057 
1058 DEFMETHOD (dbdown, interp, args, ,
1059  doc: /* -*- texinfo -*-
1060 @deftypefn {} {} dbdown
1061 @deftypefnx {} {} dbdown @var{n}
1062 In debugging mode, move down the execution stack @var{n} frames.
1063 
1064 If @var{n} is omitted, move down one frame.
1065 @seealso{dbstack, dbup}
1066 @end deftypefn */)
1067 {
1068  do_dbupdown (interp, args, "dbdown");
1069 
1070  return ovl ();
1071 }
1072 
1073 DEFMETHOD (dbstep, interp, args, ,
1074  doc: /* -*- texinfo -*-
1075 @deftypefn {} {} dbstep
1076 @deftypefnx {} {} dbstep @var{n}
1077 @deftypefnx {} {} dbstep in
1078 @deftypefnx {} {} dbstep out
1079 @deftypefnx {} {} dbnext @dots{}
1080 In debugging mode, execute the next @var{n} lines of code.
1081 
1082 If @var{n} is omitted, execute the next single line of code. If the next
1083 line of code is itself defined in terms of an m-file remain in the existing
1084 function.
1085 
1086 Using @code{dbstep in} will cause execution of the next line to step into
1087 any m-files defined on the next line.
1088 
1089 Using @code{dbstep out} will cause execution to continue until the current
1090 function returns.
1091 
1092 Programming Note: @code{dbnext} is an alias for @code{dbstep} and can be used
1093 interchangeably.
1094 @seealso{dbcont, dbquit}
1095 @end deftypefn */)
1096 {
1097  octave::tree_evaluator& tw = interp.get_evaluator ();
1098 
1099  if (! tw.in_debug_repl ())
1100  error ("dbstep: can only be called in debug mode");
1101 
1102  int nargin = args.length ();
1103 
1104  if (nargin > 1)
1105  print_usage ();
1106 
1107  int n = 0;
1108 
1109  if (nargin == 1)
1110  {
1111  std::string arg
1112  = args(0).xstring_value ("dbstep: input argument must be a string");
1113 
1114  if (arg == "in")
1115  n = -1;
1116  else if (arg == "out")
1117  n = -2;
1118  else
1119  {
1120  n = atoi (arg.c_str ());
1121 
1122  if (n < 1)
1123  error ("dbstep: invalid argument");
1124  }
1125  }
1126  else
1127  n = 1;
1128 
1129  if (n != 0)
1130  {
1131  tw.set_dbstep_flag (n);
1132 
1133  // If we set the dbstep flag, we also need to reset debug_mode.
1134  tw.reset_debug_state ();
1135 
1136  }
1137 
1138  return ovl ();
1139 }
1140 
1141 DEFALIAS (dbnext, dbstep);
1142 
1143 DEFMETHOD (dbcont, interp, args, ,
1144  doc: /* -*- texinfo -*-
1145 @deftypefn {} {} dbcont
1146 Leave command-line debugging mode and continue code execution normally.
1147 @seealso{dbstep, dbquit}
1148 @end deftypefn */)
1149 {
1150  octave::tree_evaluator& tw = interp.get_evaluator ();
1151 
1152  if (! tw.in_debug_repl ())
1153  error ("dbcont: can only be called in debug mode");
1154 
1155  if (args.length () != 0)
1156  print_usage ();
1157 
1158  tw.dbcont ();
1159 
1160  return ovl ();
1161 }
1162 
1163 DEFMETHOD (dbquit, interp, args, ,
1164  doc: /* -*- texinfo -*-
1165 @deftypefn {} {} dbquit
1166 @deftypefnx {} {} dbquit all
1167 Quit debugging mode immediately without further code execution.
1168 
1169 With no arguments, exit the current debugging level. With argument @code{all},
1170 exit all debugging levels and return to the Octave prompt.
1171 @seealso{dbcont, dbstep}
1172 @end deftypefn */)
1173 {
1174  octave::tree_evaluator& tw = interp.get_evaluator ();
1175 
1176  if (! tw.in_debug_repl ())
1177  error ("dbquit: can only be called in debug mode");
1178 
1179  int nargin = args.length ();
1180 
1181  if (nargin > 1)
1182  print_usage ();
1183 
1184  if (nargin == 1)
1185  {
1186  std::string arg
1187  = args(0).xstring_value ("dbquit: input argument must be a string");
1188 
1189  if (arg == "all")
1190  tw.dbquit (true);
1191  else
1192  error ("dbquit: unrecognized argument '%s'", arg.c_str ());
1193  }
1194  else
1195  tw.dbquit ();
1196 
1197  return ovl ();
1198 }
1199 
1200 DEFMETHOD (isdebugmode, interp, args, ,
1201  doc: /* -*- texinfo -*-
1202 @deftypefn {} {@var{tf} =} isdebugmode ()
1203 Return true if in debugging mode, otherwise false.
1204 @seealso{dbwhere, dbstack, dbstatus}
1205 @end deftypefn */)
1206 {
1207  if (args.length () != 0)
1208  print_usage ();
1209 
1210  octave::tree_evaluator& tw = interp.get_evaluator ();
1211 
1212  return ovl (tw.in_debug_repl ());
1213 }
1214 
1215 DEFMETHOD (__db_next_breakpoint_quiet__, interp, args, ,
1216  doc: /* -*- texinfo -*-
1217 @deftypefn {} {} __db_next_breakpoint_quiet__ ()
1218 @deftypefnx {} {} __db_next_breakpoint_quiet__ (@var{flag})
1219 Disable line info printing at the next breakpoint.
1220 
1221 With a logical argument @var{flag}, set the state on or off.
1222 @end deftypefn */)
1223 {
1224  int nargin = args.length ();
1225 
1226  if (nargin > 1)
1227  print_usage ();
1228 
1229  bool state = true;
1230 
1231  if (nargin == 1)
1232  state = args(0).bool_value ();
1233 
1234  octave::tree_evaluator& tw = interp.get_evaluator ();
1235 
1236  tw.quiet_breakpoint_flag (state);
1237 
1238  return ovl ();
1239 }
1240 
1241 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
void clear()
Definition: Array-base.cc:109
octave_idx_type numel() const
Number of elements in the array.
Definition: Array.h:414
Definition: Cell.h:43
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
std::string name() const
Definition: ov-fcn.h:206
Cell getfield(const std::string &key) const
Definition: oct-map.cc:275
const_iterator end() const
Definition: oct-map.h:298
bool isfield(const std::string &name) const
Definition: oct-map.h:326
void delete_elements(const octave::idx_vector &i)
Definition: oct-map.cc:1230
void setfield(const std::string &key, const Cell &val)
Definition: oct-map.cc:282
octave_idx_type numel() const
Definition: oct-map.h:368
void assign(const std::string &k, const Cell &val)
Definition: oct-map.h:344
const_iterator begin() const
Definition: oct-map.h:297
const Cell & contents(const_iterator p) const
Definition: oct-map.h:310
bool isempty() const
Definition: oct-map.h:370
std::string get_code_line(std::size_t line)
Definition: ov-usr-fcn.cc:100
std::string fcn_file_name() const
Definition: ov-usr-fcn.h:105
octave_idx_type length() const
Definition: ovl.h:113
Cell cell_value() const
int int_value(bool req_int=false, bool frc_str_conv=false) const
Definition: ov.h:812
bool is_string() const
Definition: ov.h:637
Matrix size()
Definition: ov.h:462
bool isempty() const
Definition: ov.h:601
bool iscell() const
Definition: ov.h:604
std::string string_value(bool force=false) const
Definition: ov.h:974
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void show_octave_dbstack()
Definition: debug.cc:967
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:988
void err_wrong_type_arg(const char *name, const char *s)
Definition: errwarn.cc:166
octave::idx_vector idx_vector
Definition: idx-vector.h:1022
interpreter & __get_interpreter__()
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:621
T octave_idx_type m
Definition: mx-inlines.cc:781
octave_idx_type n
Definition: mx-inlines.cc:761
std::string canonicalize_file_name(const std::string &name)
Definition: file-ops.cc:798
std::string fcn_file_in_path(const std::string &name)
Definition: utils.cc:749
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:219
#define octave_stdout
Definition: pager.h:309