GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
debug.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2001-2025 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
64static octave_value
65bp_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
79DEFMETHOD (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
94Set breakpoints for the built-in debugger.
95
96@var{fcn} is the name of a function on the current @code{path}. When
97already in debug mode the @var{fcn} argument can be omitted and the current
98function will be used. Breakpoints at subfunctions are set with the scope
99operator @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
104specified, it defaults to the first executable line in the file
105@file{fcn.m}. Multiple lines can be specified in a single command; when
106function syntax is used, the lines may also be passed as a single vector
107argument (@code{[@var{line1}, @var{line2}, @dots{}]}).
108
109@var{condition} is any Octave expression that can be evaluated in the code
110context 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
113example because it refers to an undefined variable, an error will be thrown.
114Expressions with side effects (such as @code{y++ > 1}) will alter variables,
115and should generally be avoided. Conditions containing quotes (@samp{"},
116@samp{'}) or comment characters (@samp{#}, @samp{%}) must be enclosed in
117quotes. (This does not apply to conditions entered from the editor's context
118menu.) For example:
119
120@example
121dbstop in axis at 246 if 'any (opt == "x")'
122@end example
123
124The form specifying @var{event} does not cause a specific breakpoint at a given
125function and line number. Instead it causes debug mode to be entered when
126certain unexpected events are encountered. Possible values are
127
128@table @code
129@item error
130Stop 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
134Stop when an error is caught by a try-catch block (not yet implemented).
135
136@item interrupt
137Stop when an interrupt (@kbd{Ctrl-C}) occurs.
138
139@item naninf
140Stop when code returns a non-finite value (not yet implemented).
141
142@item warning
143Stop when a warning is reported. This is equivalent to specifying
144@code{debug_on_warning (true)}.
145@end table
146
147The events @code{error}, @code{caught error}, and @code{warning} can all be
148followed by a string specifying an error ID or warning ID@. If that is done,
149only errors with the specified ID will cause execution to stop. To stop on one
150of a set of IDs, multiple @code{dbstop} commands must be issued.
151
152Breakpoints and events can be removed using the @code{dbclear} command with
153the same syntax.
154
155It is possible to save all breakpoints and restore them at once by issuing
156the commands @code{bp_state = dbstatus; @dots{}; dbstop (bp_state)}.
157
158The optional output @var{rline} is the real line number where the breakpoint
159was set. This can differ from the specified line if the line is not
160executable. For example, if a breakpoint attempted on a blank line then Octave
161will set the real breakpoint at the next executable line.
162
163When a file is re-parsed, such as when it is modified outside the GUI, all
164breakpoints within the file are cleared.
165
166@seealso{dbclear, dbstatus, dbstep, debug_on_error, debug_on_warning,
167debug_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
266DEFMETHOD (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")
282Delete a breakpoint at line number @var{line} in the function @var{fcn}.
283
284Arguments are
285
286@table @var
287@item fcn
288Function name as a string variable. When already in debug mode this
289argument can be omitted and the current function will be used.
290
291@item line
292Line number from which to remove a breakpoint. Multiple lines may be given
293as separate arguments or as a vector.
294
295@item event
296An event such as @code{error}, @code{interrupt}, or @code{warning}
297(@pxref{XREFdbstop,,@code{dbstop}} for details).
298@end table
299
300When called without a line number specification all breakpoints in the named
301function are cleared.
302
303If the requested line is not a breakpoint no action is performed.
304
305The special keyword @qcode{"all"} will clear all breakpoints from all
306files.
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
344DEFMETHOD (dbstatus, interp, args, nargout,
345 doc: /* -*- texinfo -*-
346@deftypefn {} {} dbstatus
347@deftypefnx {} {} dbstatus @var{fcn}
348@deftypefnx {} {@var{bp_list} =} dbstatus @dots{}
349Report the location of active breakpoints.
350
351When called with no input or output arguments, print the list of all
352functions with breakpoints and the line numbers where those breakpoints are
353set.
354
355If a function name @var{fcn} is specified then only report breakpoints
356for the named function and its subfunctions.
357
358The optional return argument @var{bp_list} is a struct array with the
359following fields:
360
361@table @asis
362@item name
363The name of the function with a breakpoint. A subfunction, say @code{fcn2}
364within an m-file, say @file{file.m}, is specified as @code{file>fcn2}.
365
366@item file
367The name of the m-file where the function code is located.
368
369@item line
370The line number with the breakpoint.
371
372@item cond
373The condition that must be satisfied for the breakpoint to be active, or
374the 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.
380If @code{dbstop if error} is true but no explicit IDs are specified, the
381return value will have an empty field called @qcode{"errs"}. If IDs are
382specified, the @code{errs} field will have one row per ID@. If
383@code{dbstop if error} is false, there is no @qcode{"errs"} field.
384The @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 286;
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, 280);
567%! assert (s(4).name, "@inputParser/addOptional");
568%! assert (s(4).line, 286);
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
580DEFMETHOD (dbwhere, interp, , ,
581 doc: /* -*- texinfo -*-
582@deftypefn {} {} dbwhere
583In debugging mode, report the current file and line number where execution
584is 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
595static bool
596parse_start_end (const std::string& arg, int& start, int& end, const char *who)
597{
598 start = 0;
599 end = 0;
600
601 std::size_t ind = arg.find (':');
602
603 if (ind != std::string::npos) // (start:end)
604 {
605 std::string start_str = arg.substr (0, ind);
606 std::string end_str = arg.substr (ind+1);
607
608 try
609 {
610 start = std::stoi (start_str);
611
612 if (end_str == "end")
613 end = std::numeric_limits<int>::max ();
614 else
615 end = std::stoi (end_str);
616 }
617 catch (const std::invalid_argument&)
618 {
619 error ("%s: invalid integer conversion while parsing range '%s'", who, arg.c_str ());
620 }
621 catch (const std::out_of_range&)
622 {
623 error ("%s: integer value out of bounds while parsing range '%s'", who, arg.c_str ());
624 }
625
626 if (std::min (start, end) <= 0)
627 error ("%s: start and end lines must be >= 1\n", who);
628
629 if (start > end)
630 error ("%s: start line must be less than end line\n", who);
631 }
632 else // (dbtype lineno)
633 {
634 try
635 {
636 int line = std::stoi (arg);
637
638 if (line <= 0)
639 error ("%s: start and end lines must be >= 1\n", who);
640
641 start = line;
642 end = line;
643 }
644 catch (const std::invalid_argument&)
645 {
646 // May be a name instead of a number.
647 return false;
648 }
649 catch (const std::out_of_range&)
650 {
651 error ("%s: integer value out of bounds while parsing '%s'", who, arg.c_str ());
652 }
653 }
654
655 return true;
656}
657
658DEFMETHOD (dbtype, interp, args, ,
659 doc: /* -*- texinfo -*-
660@deftypefn {} {} dbtype
661@deftypefnx {} {} dbtype @var{lineno}
662@deftypefnx {} {} dbtype @var{startl:endl}
663@deftypefnx {} {} dbtype @var{startl}:end
664@deftypefnx {} {} dbtype @var{fcn}
665@deftypefnx {} {} dbtype @var{fcn} @var{lineno}
666@deftypefnx {} {} dbtype @var{fcn} @var{startl:endl}
667@deftypefnx {} {} dbtype @var{fcn} @var{startl}:end
668Display a script file with line numbers.
669
670When called with no arguments in debugging mode, display the script file
671currently being debugged.
672
673An optional range specification can be used to list only a portion of the
674file. The special keyword @qcode{"end"} is a valid line number specification
675for the last line of the file.
676
677When called with the name of a function, list that script file with line
678numbers.
679@seealso{dblist, dbwhere, dbstatus, dbstop}
680@end deftypefn */)
681{
682 string_vector argv = args.make_argv ("dbtype");
683
684 // Empty means current function on call stack.
685 std::string fcn_name;
686
687 int start = 0;
688 int end = std::numeric_limits<int>::max ();
689
690 switch (args.length ())
691 {
692 case 0: // dbtype
693 break;
694
695 case 1: // (dbtype start:end) || (dbtype fcn) || (dbtype lineno)
696 {
697 std::string arg = argv[1];
698
699 if (! parse_start_end (arg, start, end, "dbtype"))
700 fcn_name = arg;
701 }
702 break;
703
704 case 2: // (dbtype fcn start:end) || (dbtype fcn start)
705 {
706 fcn_name = argv[1];
707
708 if (! parse_start_end (argv[2], start, end, "dbtype"))
709 error ("dbtype: expecting start:end or location argument, found '%s'", argv[2].c_str ());
710 }
711 break;
712
713 default:
714 error ("dbtype: expecting zero, one, or two arguments\n");
715 }
716
717 if (fcn_name.empty ())
718 {
719 octave::tree_evaluator& tw = interp.get_evaluator ();
720
721 tw.debug_type (octave_stdout, start, end);
722 }
723 else
724 {
725 std::string file_name = octave::fcn_file_in_path (fcn_name);
726
727 if (file_name.empty ())
728 error ("dbtype: unknown function '%s'", fcn_name.c_str ());
729
730 octave::display_file_lines (octave_stdout, file_name, start, end, -1, "", "dbtype");
731 }
732
733 return ovl ();
734}
735
736static int
737parse_integer_argument (const std::string& arg, const char *who)
738{
739 int n = 0;
740
741 try
742 {
743 n = std::stoi (arg);
744 }
745 catch (const std::invalid_argument&)
746 {
747 error ("%s: invalid value of N, found '%s'", arg.c_str (), who);
748 }
749 catch (const std::out_of_range&)
750 {
751 error ("%s: value of N ('%s') is out of range", arg.c_str (), who);
752 }
753
754 return n;
755}
756
757DEFMETHOD (dblist, interp, args, ,
758 doc: /* -*- texinfo -*-
759@deftypefn {} {} dblist
760@deftypefnx {} {} dblist @var{n}
761In debugging mode, list @var{n} lines of the function being debugged
762centered around the current line to be executed.
763
764If unspecified @var{n} defaults to 10 (+/- 5 lines)
765@seealso{dbwhere, dbtype, dbstack}
766@end deftypefn */)
767{
768 int n = 10;
769
770 int numel = args.length ();
771
772 if (numel > 1)
773 print_usage ();
774
775 if (numel == 1)
776 {
777 octave_value arg = args(0);
778
779 if (arg.is_string ())
780 n = parse_integer_argument (arg.string_value (), "dblist");
781 else
782 n = args(0).int_value ();
783
784 if (n < 0)
785 error ("dblist: N must be a non-negative integer");
786 }
787
788 octave::tree_evaluator& tw = interp.get_evaluator ();
789
790 tw.debug_list (octave_stdout, n);
791
792 return ovl ();
793}
794
796do_dbstack (octave::interpreter& interp, const octave_value_list& args,
797 int nargout, std::ostream& os)
798{
799 int nargin = args.length ();
800
801 if (nargin > 2)
802 print_usage ();
803
804 octave_value_list retval;
805
806 octave_idx_type curr_frame = -1;
807
808 octave_idx_type nskip = 0;
809
810 if (nargin == 1 || nargin == 2)
811 {
812 int n = 0;
813
814 for (octave_idx_type i = 0; i < nargin; i++)
815 {
816 octave_value arg = args(i);
817
818 if (arg.is_string ())
819 {
820 std::string s_arg = arg.string_value ();
821
822 // Skip "-completenames", octave returns full names anyway.
823 if (s_arg == "-completenames")
824 continue;
825
826 n = parse_integer_argument (s_arg, "dbstack");
827 }
828 else
829 n = arg.int_value ();
830
831 if (n < 0)
832 error ("dbstack: N must be a non-negative integer");
833 }
834
835 if (n > 0)
836 nskip = n;
837 }
838
839 octave::tree_evaluator& tw = interp.get_evaluator ();
840
841 if (nargout == 0)
842 {
843 octave_map stk = tw.backtrace (curr_frame);
844 octave_idx_type nframes = stk.numel ();
845
846 if (nframes > 0)
847 {
848 octave::preserve_stream_state stream_state (os);
849
850 os << "stopped in:\n\n";
851
852 Cell names = stk.contents ("name");
853 Cell files = stk.contents ("file");
854 Cell lines = stk.contents ("line");
855
856 bool show_top_level = true;
857
858 std::size_t max_name_len = 0;
859
860 for (octave_idx_type i = nskip; i < nframes; i++)
861 {
862 std::string name = names(i).string_value ();
863
864 max_name_len = std::max (name.length (), max_name_len);
865 }
866
867 for (octave_idx_type i = nskip; i < nframes; i++)
868 {
869 std::string name = names(i).string_value ();
870 std::string file = files(i).string_value ();
871 int line = lines(i).int_value ();
872
873 if (show_top_level && i == curr_frame)
874 show_top_level = false;
875
876 os << (i == curr_frame ? " --> " : " ")
877 << std::setw (max_name_len) << name
878 << " at line " << line
879 << " [" << file << ']'
880 << std::endl;
881 }
882
883 if (tw.at_top_level () && show_top_level)
884 os << " --> top level" << std::endl;
885 }
886 }
887 else
888 {
889 octave_map stk = tw.backtrace (curr_frame, false);
890
891 octave_idx_type num_skip = std::min (nskip, stk.numel ());
892
893 idx_vector first = idx_vector (0);
894
895 for (octave_idx_type i = 0; i < num_skip; i++)
896 stk.delete_elements (first);
897
898 curr_frame -= num_skip;
899 curr_frame = (curr_frame < 0 ? 0 : curr_frame + 1);
900
901 retval = ovl (stk, curr_frame);
902 }
903
904 return retval;
905}
906
907// A function that can be easily called from a debugger print the Octave stack.
908// This can be useful for finding what line of code the interpreter is
909// currently executing when the debugger is stopped in some C++ function,
910// for example.
911
912void
914{
915 do_dbstack (octave::__get_interpreter__ (),
916 octave_value_list (), 0, std::cerr);
917}
918
919DEFMETHOD (dbstack, interp, args, nargout,
920 doc: /* -*- texinfo -*-
921@deftypefn {} {} dbstack
922@deftypefnx {} {} dbstack @var{n}
923@deftypefnx {} {} dbstack @var{-completenames}
924@deftypefnx {} {[@var{stack}, @var{idx}] =} dbstack (@dots{})
925Display or return current debugging function stack information.
926
927With optional argument @var{n}, omit the @var{n} innermost stack frames.
928
929Although accepted, the argument @var{-completenames} is silently ignored.
930Octave always returns absolute filenames.
931
932The arguments @var{n} and @var{-completenames} can both be specified and may
933appear in any order.
934
935The optional return argument @var{stack} is a struct array with the
936following fields:
937
938@table @asis
939@item file
940The name of the m-file where the function code is located.
941
942@item name
943The name of the function with a breakpoint.
944
945@item line
946The line number of an active breakpoint.
947
948@item column
949The column number of the line where the breakpoint begins.
950
951@end table
952
953The return argument @var{idx} specifies which element of the @var{stack}
954struct array is currently active.
955@seealso{dbup, dbdown, dbwhere, dblist, dbstatus}
956@end deftypefn */)
957{
958 return do_dbstack (interp, args, nargout, octave_stdout);
959}
960
961static void
962do_dbupdown (octave::interpreter& interp, const octave_value_list& args,
963 const std::string& who)
964{
965 int n = 1;
966
967 if (args.length () == 1)
968 {
969 octave_value arg = args(0);
970
971 if (arg.is_string ())
972 n = parse_integer_argument (arg.string_value (), who.c_str ());
973 else
974 n = args(0).int_value ();
975 }
976
977 if (who == "dbup")
978 n = -n;
979
980 octave::tree_evaluator& tw = interp.get_evaluator ();
981
982 tw.dbupdown (n, true);
983}
984
985DEFMETHOD (dbup, interp, args, ,
986 doc: /* -*- texinfo -*-
987@deftypefn {} {} dbup
988@deftypefnx {} {} dbup @var{n}
989In debugging mode, move up the execution stack @var{n} frames.
990
991If @var{n} is omitted, move up one frame.
992@seealso{dbstack, dbdown}
993@end deftypefn */)
994{
995 do_dbupdown (interp, args, "dbup");
996
997 return ovl ();
998}
999
1000DEFMETHOD (dbdown, interp, args, ,
1001 doc: /* -*- texinfo -*-
1002@deftypefn {} {} dbdown
1003@deftypefnx {} {} dbdown @var{n}
1004In debugging mode, move down the execution stack @var{n} frames.
1005
1006If @var{n} is omitted, move down one frame.
1007@seealso{dbstack, dbup}
1008@end deftypefn */)
1009{
1010 do_dbupdown (interp, args, "dbdown");
1011
1012 return ovl ();
1013}
1014
1015DEFMETHOD (dbstep, interp, args, ,
1016 doc: /* -*- texinfo -*-
1017@deftypefn {} {} dbstep
1018@deftypefnx {} {} dbstep @var{n}
1019@deftypefnx {} {} dbstep in
1020@deftypefnx {} {} dbstep out
1021@deftypefnx {} {} dbnext @dots{}
1022In debugging mode, execute the next @var{n} lines of code.
1023
1024If @var{n} is omitted, execute the next single line of code. If the next
1025line of code is itself defined in terms of an m-file remain in the existing
1026function.
1027
1028Using @code{dbstep in} will cause execution of the next line to step into
1029any m-files defined on the next line.
1030
1031Using @code{dbstep out} will cause execution to continue until the current
1032function returns.
1033
1034Programming Note: @code{dbnext} is an alias for @code{dbstep} and can be used
1035interchangeably.
1036@seealso{dbcont, dbquit}
1037@end deftypefn */)
1038{
1039 octave::tree_evaluator& tw = interp.get_evaluator ();
1040
1041 if (! tw.in_debug_repl ())
1042 error ("dbstep: can only be called in debug mode");
1043
1044 int nargin = args.length ();
1045
1046 if (nargin > 1)
1047 print_usage ();
1048
1049 int n = 0;
1050
1051 if (nargin == 1)
1052 {
1053 std::string arg
1054 = args(0).xstring_value ("dbstep: input argument must be a string");
1055
1056 if (arg == "in")
1057 n = -1;
1058 else if (arg == "out")
1059 n = -2;
1060 else
1061 {
1062 n = parse_integer_argument (arg, "dbstep");
1063
1064 if (n < 1)
1065 error ("dbstep: N must be greater than zero");
1066 }
1067 }
1068 else
1069 n = 1;
1070
1071 if (n != 0)
1072 {
1073 tw.set_dbstep_flag (n);
1074
1075 // If we set the dbstep flag, we also need to reset debug_mode.
1076 tw.reset_debug_state ();
1077
1078 }
1079
1080 return ovl ();
1081}
1082
1083DEFALIAS (dbnext, dbstep);
1084
1085DEFMETHOD (dbcont, interp, args, ,
1086 doc: /* -*- texinfo -*-
1087@deftypefn {} {} dbcont
1088Leave command-line debugging mode and continue code execution normally.
1089@seealso{dbstep, dbquit}
1090@end deftypefn */)
1091{
1092 octave::tree_evaluator& tw = interp.get_evaluator ();
1093
1094 if (! tw.in_debug_repl ())
1095 error ("dbcont: can only be called in debug mode");
1096
1097 if (args.length () != 0)
1098 print_usage ();
1099
1100 tw.dbcont ();
1101
1102 return ovl ();
1103}
1104
1105DEFMETHOD (dbquit, interp, args, ,
1106 doc: /* -*- texinfo -*-
1107@deftypefn {} {} dbquit
1108@deftypefnx {} {} dbquit all
1109Quit debugging mode immediately without further code execution.
1110
1111With no arguments, exit the current debugging level. With argument @code{all},
1112exit all debugging levels and return to the Octave prompt.
1113@seealso{dbcont, dbstep}
1114@end deftypefn */)
1115{
1116 octave::tree_evaluator& tw = interp.get_evaluator ();
1117
1118 if (! tw.in_debug_repl ())
1119 error ("dbquit: can only be called in debug mode");
1120
1121 int nargin = args.length ();
1122
1123 if (nargin > 1)
1124 print_usage ();
1125
1126 if (nargin == 1)
1127 {
1128 std::string arg
1129 = args(0).xstring_value ("dbquit: input argument must be a string");
1130
1131 if (arg == "all")
1132 tw.dbquit (true);
1133 else
1134 error ("dbquit: unrecognized argument '%s'", arg.c_str ());
1135 }
1136 else
1137 tw.dbquit ();
1138
1139 return ovl ();
1140}
1141
1142DEFMETHOD (isdebugmode, interp, args, ,
1143 doc: /* -*- texinfo -*-
1144@deftypefn {} {@var{tf} =} isdebugmode ()
1145Return true if in debugging mode, otherwise false.
1146@seealso{dbwhere, dbstack, dbstatus}
1147@end deftypefn */)
1148{
1149 if (args.length () != 0)
1150 print_usage ();
1151
1152 octave::tree_evaluator& tw = interp.get_evaluator ();
1153
1154 return ovl (tw.in_debug_repl ());
1155}
1156
1157DEFMETHOD (__db_next_breakpoint_quiet__, interp, args, ,
1158 doc: /* -*- texinfo -*-
1159@deftypefn {} {} __db_next_breakpoint_quiet__ ()
1160@deftypefnx {} {} __db_next_breakpoint_quiet__ (@var{flag})
1161Disable line info printing at the next breakpoint.
1162
1163With a logical argument @var{flag}, set the state on or off.
1164@end deftypefn */)
1165{
1166 int nargin = args.length ();
1167
1168 if (nargin > 1)
1169 print_usage ();
1170
1171 bool state = true;
1172
1173 if (nargin == 1)
1174 state = args(0).bool_value ();
1175
1176 octave::tree_evaluator& tw = interp.get_evaluator ();
1177
1178 tw.quiet_breakpoint_flag (state);
1179
1180 return ovl ();
1181}
1182
1183OCTAVE_END_NAMESPACE(octave)
void clear()
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
Definition Cell.h:41
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:90
Cell getfield(const std::string &key) const
Definition oct-map.cc:275
const_iterator end() const
Definition oct-map.h:298
const Cell & contents(const_iterator p) const
Definition oct-map.h:310
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
bool isempty() const
Definition oct-map.h:370
octave_idx_type length() const
Definition ovl.h:111
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
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:983
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void show_octave_dbstack()
Definition debug.cc:913
void print_usage()
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:1003
void err_wrong_type_arg(const char *name, const char *s)
Definition errwarn.cc:166
F77_RET_T const F77_DBLE const F77_DBLE * f
T::size_type numel(const T &str)
Definition oct-string.cc:74
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217
#define octave_stdout
Definition pager.h:301