GNU Octave 11.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-2026 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 "oct-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 ()
349@deftypefnx {} {@var{bp_list} =} dbstatus (@var{fcn})
350Report the location of active breakpoints.
351
352When called with no input or output arguments, print the list of all
353functions with breakpoints and the line numbers where those breakpoints are
354set.
355
356If a function name @var{fcn} is specified then only report breakpoints
357for the named function and its subfunctions.
358
359The optional return argument @var{bp_list} is a struct array with the
360following fields:
361
362@table @asis
363@item name
364The name of the function with a breakpoint. A subfunction, say @code{fcn2}
365within an m-file, say @file{file.m}, is specified as @code{file>fcn2}.
366
367@item file
368The name of the m-file where the function code is located.
369
370@item line
371The line number with the breakpoint.
372
373@item cond
374The condition that must be satisfied for the breakpoint to be active, or
375the empty string for unconditional breakpoints.
376@end table
377
378@c Note: When @code{dbstatus} is called from the debug prompt within a function,
379@c the list of breakpoints is automatically trimmed to the breakpoints in the
380@c current function.
381If @code{dbstop if error} is true but no explicit IDs are specified, the
382return value will have an empty field called @qcode{"errs"}. If IDs are
383specified, the @code{errs} field will have one row per ID@. If
384@code{dbstop if error} is false, there is no @qcode{"errs"} field.
385The @qcode{"warn"} field is set similarly by @code{dbstop if warning}.
386
387@seealso{dbstop, dbclear, dbwhere, dblist, dbstack}
388@end deftypefn */)
389{
390 int nargin = args.length ();
391
392 if (nargin != 0 && nargin != 1)
393 error ("dbstatus: only zero or one arguments accepted\n");
394
395 octave_value_list fcn_list;
396 octave::bp_table::fname_bp_map bp_list;
397 std::string symbol_name;
398
399 octave::tree_evaluator& tw = interp.get_evaluator ();
400
401 octave::bp_table& bptab = tw.get_bp_table ();
402
403 if (nargin == 1)
404 {
405 if (! args(0).is_string ())
406 err_wrong_type_arg ("dbstatus", args(0));
407
408 symbol_name = args(0).string_value ();
409 fcn_list(0) = symbol_name;
410 bp_list = bptab.get_breakpoint_list (fcn_list);
411 }
412 else
413 {
414 /*
415 if (tw.in_debug_repl ())
416 {
417 octave_user_code *dbg_fcn = tw.get_user_code ();
418 if (dbg_fcn)
419 {
420 symbol_name = dbg_fcn->name ();
421 fcn_list(0) = symbol_name;
422 }
423 }
424 */
425
426 bp_list = bptab.get_breakpoint_list (fcn_list);
427 }
428
429 if (nargout == 0)
430 {
431 // Print out the breakpoint information.
432
433 for (auto& fnm_bp_p: bp_list)
434 {
435 std::list<octave::bp_type> m = fnm_bp_p.second;
436
437 // print unconditional breakpoints, if any, on a single line
438
439 // first, check to see if there are any
440 int have_unconditional = 0;
441 for (const auto& bp : m)
442 {
443 if (bp.cond == "")
444 {
445 if (have_unconditional++)
446 break; // stop once we know its plural
447 }
448 }
449 // If we actually have some, print line numbers only
450 if (have_unconditional)
451 {
452 const char *_s_ = (have_unconditional > 1) ? "s" : "";
453 octave_stdout << "breakpoint" << _s_ << " in " << fnm_bp_p.first
454 << " at line" << _s_ << ' ';
455
456 for (const auto& bp : m)
457 {
458 if (bp.cond == "")
459 octave_stdout << bp.line << ' ';
460 }
461 octave_stdout << std::endl;
462 }
463
464 // print conditional breakpoints, one per line, with conditions
465 for (const auto& bp : m)
466 {
467 if (bp.cond != "")
468 octave_stdout << "breakpoint in " << fnm_bp_p.first
469 << " at line " << bp.line
470 << " if " << bp.cond << "\n";
471 }
472 }
473
474 bptab.stop_on_err_warn_status (true);
475
476 return ovl ();
477 }
478 else
479 {
480 octave::help_system& help_sys = interp.get_help_system ();
481
482 // Fill in an array for return.
483 int i = 0;
484 octave_map retmap;
485 octave_value retval;
486
487 // count the number of breakpoints in all files
488 int count = 0;
489 for (const auto& fnm_bp_p : bp_list)
490 count += fnm_bp_p.second.size ();
491
492 Cell names (dim_vector (count, 1));
493 Cell file (dim_vector (count, 1));
494 Cell line (dim_vector (count, 1));
495 Cell cond (dim_vector (count, 1));
496
497 for (const auto& fnm_bp_p : bp_list)
498 {
499 std::string filename = fnm_bp_p.first;
500 const char *sub_fcn = strchr (filename.c_str (), '>');
501 if (sub_fcn)
502 filename = filename.substr(0, sub_fcn - filename.c_str ());
503 octave_value path_name;
504 path_name
505 = octave::sys::canonicalize_file_name (help_sys.which (filename));
506
507 for (const auto& bp : fnm_bp_p.second)
508 {
509 names(i) = fnm_bp_p.first;
510 file(i) = path_name;
511 line(i) = octave_value (bp.line);
512 cond(i) = octave_value (bp.cond);
513 i++;
514 }
515 }
516
517 retmap.assign ("name", names);
518 retmap.assign ("file", file);
519 retmap.assign ("line", line);
520 retmap.assign ("cond", cond);
521
522 const octave_map ew = bptab.stop_on_err_warn_status (false);
523 if (ew.isempty ())
524 {
525 retval = octave_value (retmap);
526 }
527 else
528 {
529 octave_map outer (dim_vector (3, 1));
530 outer.assign ("bkpt", Cell (retmap));
531 for (auto f = ew.begin (); f != ew.end (); f++)
532 outer.setfield (f->first, ew.contents (f));
533
534 retval = octave_value (outer);
535 }
536
537 return retval;
538 }
539}
540
541/*
542%!test
543%! if (isguirunning ())
544%! orig_show_dbg = __event_manager_gui_preference__ ("editor/show_dbg_file",
545%! "false");
546%! endif
547%! unwind_protect
548%! dbclear all; # Clear out breakpoints before test
549%! dbstop @ftp/dir;
550%! dbstop @audioplayer/set 75;
551%! dbstop quantile>__quantile__;
552%! dbstop ls;
553%! dbstop in inputParser at addOptional;
554%! dbstop in inputParser at 286;
555%! s = dbstatus ();
556%! dbclear all;
557%! ## For Matlab compatibility, the following name should be:
558%! ## audioplayer.set>setproperty
559%! assert (s(1).name, "@audioplayer/set>setproperty");
560%! ## For Matlab compatibility, the following name should be:
561%! ## ftp.dir
562%! assert (s(2).name, "@ftp/dir");
563%! assert (s(2).file(end-10:end), [filesep "@ftp" filesep "dir.m"]);
564%! ## For Matlab compatibility, the following two names should be:
565%! ## inputParser.inputParser>inputParser.addOptional
566%! assert (s(3).name, "@inputParser/addOptional");
567%! assert (s(3).line, 280);
568%! assert (s(4).name, "@inputParser/addOptional");
569%! assert (s(4).line, 286);
570%! assert (s(5).name, "ls");
571%! assert (s(6).name, "quantile>__quantile__");
572%! s = dbstatus ();
573%! assert (isempty (s));
574%! unwind_protect_cleanup
575%! if (isguirunning ())
576%! __event_manager_gui_preference__ ("editor/show_dbg_file", orig_show_dbg);
577%! endif
578%! end_unwind_protect
579*/
580
581DEFMETHOD (dbwhere, interp, , ,
582 doc: /* -*- texinfo -*-
583@deftypefn {} {} dbwhere
584In debugging mode, report the current file and line number where execution
585is stopped.
586@seealso{dbstack, dblist, dbstatus, dbcont, dbstep, dbup, dbdown}
587@end deftypefn */)
588{
589 octave::tree_evaluator& tw = interp.get_evaluator ();
590
591 tw.debug_where (octave_stdout);
592
593 return ovl ();
594}
595
596static bool
597parse_start_end (const std::string& arg, int& start, int& end, const char *who)
598{
599 std::size_t idx = arg.find (':');
600
601 if (idx != std::string::npos) // (start:end)
602 {
603 std::string start_str = arg.substr (0, idx);
604 std::string end_str = arg.substr (idx+1);
605
606 try
607 {
608 start = std::stoi (start_str);
609
610 if (end_str == "end")
611 end = std::numeric_limits<int>::max ();
612 else
613 end = std::stoi (end_str);
614 }
615 catch (const std::invalid_argument&)
616 {
617 error ("%s: invalid integer conversion while parsing range '%s'", who, arg.c_str ());
618 }
619 catch (const std::out_of_range&)
620 {
621 error ("%s: integer value out of bounds while parsing range '%s'", who, arg.c_str ());
622 }
623
624 if (std::min (start, end) <= 0)
625 error ("%s: start and end lines must be >= 1\n", who);
626
627 if (start > end)
628 error ("%s: start line must be less than end line\n", who);
629 }
630 else // (dbtype lineno)
631 {
632 try
633 {
634 int line = std::stoi (arg);
635
636 if (line <= 0)
637 error ("%s: start and end lines must be >= 1\n", who);
638
639 start = end = line;
640 }
641 catch (const std::invalid_argument&)
642 {
643 // May be a name instead of a number.
644 return false;
645 }
646 catch (const std::out_of_range&)
647 {
648 error ("%s: integer value out of bounds while parsing '%s'", who, arg.c_str ());
649 }
650 }
651
652 return true;
653}
654
655DEFMETHOD (dbtype, interp, args, ,
656 doc: /* -*- texinfo -*-
657@deftypefn {} {} dbtype
658@deftypefnx {} {} dbtype @var{lineno}
659@deftypefnx {} {} dbtype @var{startl:endl}
660@deftypefnx {} {} dbtype @var{startl}:end
661@deftypefnx {} {} dbtype @var{fcn}
662@deftypefnx {} {} dbtype @var{fcn} @var{lineno}
663@deftypefnx {} {} dbtype @var{fcn} @var{startl:endl}
664@deftypefnx {} {} dbtype @var{fcn} @var{startl}:end
665Display a script file with line numbers.
666
667When called with no arguments in debugging mode, display the script file
668currently being debugged.
669
670An optional range specification can be used to list only a portion of the
671file. The special keyword @qcode{"end"} is a valid line number specification
672for the last line of the file.
673
674When called with the name of a function, list that script file with line
675numbers.
676@seealso{dblist, dbwhere, dbstatus, dbstop}
677@end deftypefn */)
678{
679 string_vector argv = args.make_argv ("dbtype");
680
681 // Empty means current function on call stack.
682 std::string fcn_name;
683
684 int start = 0;
685 int end = std::numeric_limits<int>::max ();
686
687 switch (args.length ())
688 {
689 case 0: // dbtype
690 break;
691
692 case 1: // (dbtype start:end) || (dbtype fcn) || (dbtype lineno)
693 {
694 std::string arg = argv[1];
695
696 if (! parse_start_end (arg, start, end, "dbtype"))
697 fcn_name = arg;
698 }
699 break;
700
701 case 2: // (dbtype fcn start:end) || (dbtype fcn start)
702 {
703 fcn_name = argv[1];
704
705 if (! parse_start_end (argv[2], start, end, "dbtype"))
706 error ("dbtype: expecting start:end or location argument, found '%s'", argv[2].c_str ());
707 }
708 break;
709
710 default:
711 error ("dbtype: expecting zero, one, or two arguments\n");
712 }
713
714 if (fcn_name.empty ())
715 {
716 octave::tree_evaluator& tw = interp.get_evaluator ();
717
718 tw.debug_type (octave_stdout, start, end);
719 }
720 else
721 {
722 std::string file_name = octave::fcn_file_in_path (fcn_name);
723
724 if (file_name.empty ())
725 error ("dbtype: unknown function '%s'", fcn_name.c_str ());
726
727 octave::display_file_lines (octave_stdout, file_name, start, end, -1, "", "dbtype");
728 }
729
730 return ovl ();
731}
732
733static int
734parse_integer_argument (const std::string& arg, const char *who)
735{
736 int n = 0;
737
738 try
739 {
740 n = std::stoi (arg);
741 }
742 catch (const std::invalid_argument&)
743 {
744 error ("%s: invalid value of N, found '%s'", arg.c_str (), who);
745 }
746 catch (const std::out_of_range&)
747 {
748 error ("%s: value of N ('%s') is out of range", arg.c_str (), who);
749 }
750
751 return n;
752}
753
754DEFMETHOD (dblist, interp, args, ,
755 doc: /* -*- texinfo -*-
756@deftypefn {} {} dblist
757@deftypefnx {} {} dblist @var{n}
758In debugging mode, list @var{n} lines of the function being debugged
759centered around the current line to be executed.
760
761If unspecified @var{n} defaults to 10 (+/- 5 lines)
762@seealso{dbwhere, dbtype, dbstack}
763@end deftypefn */)
764{
765 int n = 10;
766
767 int numel = args.length ();
768
769 if (numel > 1)
770 print_usage ();
771
772 if (numel == 1)
773 {
774 octave_value arg = args(0);
775
776 if (arg.is_string ())
777 n = parse_integer_argument (arg.string_value (), "dblist");
778 else
779 n = args(0).int_value ();
780
781 if (n < 0)
782 error ("dblist: N must be a non-negative integer");
783 }
784
785 octave::tree_evaluator& tw = interp.get_evaluator ();
786
787 tw.debug_list (octave_stdout, n);
788
789 return ovl ();
790}
791
793do_dbstack (octave::interpreter& interp, const octave_value_list& args,
794 int nargout, std::ostream& os)
795{
796 int nargin = args.length ();
797
798 if (nargin > 2)
799 print_usage ();
800
801 octave_value_list retval;
802
803 octave_idx_type curr_frame = -1;
804
805 octave_idx_type nskip = 0;
806
807 if (nargin == 1 || nargin == 2)
808 {
809 int n = 0;
810
811 for (octave_idx_type i = 0; i < nargin; i++)
812 {
813 octave_value arg = args(i);
814
815 if (arg.is_string ())
816 {
817 std::string s_arg = arg.string_value ();
818
819 // Skip "-completenames", octave returns full names anyway.
820 if (s_arg == "-completenames")
821 continue;
822
823 n = parse_integer_argument (s_arg, "dbstack");
824 }
825 else
826 n = arg.int_value ();
827
828 if (n < 0)
829 error ("dbstack: N must be a non-negative integer");
830 }
831
832 if (n > 0)
833 nskip = n;
834 }
835
836 octave::tree_evaluator& tw = interp.get_evaluator ();
837
838 if (nargout == 0)
839 {
840 octave_map stk = tw.backtrace (curr_frame);
841 octave_idx_type nframes = stk.numel ();
842
843 if (nframes > 0)
844 {
845 octave::preserve_stream_state stream_state (os);
846
847 os << "stopped in:\n\n";
848
849 Cell names = stk.contents ("name");
850 Cell files = stk.contents ("file");
851 Cell lines = stk.contents ("line");
852
853 bool show_top_level = true;
854
855 std::size_t max_name_len = 0;
856
857 for (octave_idx_type i = nskip; i < nframes; i++)
858 {
859 std::string name = names(i).string_value ();
860
861 max_name_len = std::max (name.length (), max_name_len);
862 }
863
864 for (octave_idx_type i = nskip; i < nframes; i++)
865 {
866 std::string name = names(i).string_value ();
867 std::string file = files(i).string_value ();
868 int line = lines(i).int_value ();
869
870 if (show_top_level && i == curr_frame)
871 show_top_level = false;
872
873 os << (i == curr_frame ? " --> " : " ")
874 << std::setw (max_name_len) << name
875 << " at line " << line
876 << " [" << file << ']'
877 << std::endl;
878 }
879
880 if (tw.at_top_level () && show_top_level)
881 os << " --> top level" << std::endl;
882 }
883 }
884 else
885 {
886 octave_map stk = tw.backtrace (curr_frame, false);
887
888 octave_idx_type num_skip = std::min (nskip, stk.numel ());
889
890 idx_vector first = idx_vector (0);
891
892 for (octave_idx_type i = 0; i < num_skip; i++)
893 stk.delete_elements (first);
894
895 curr_frame -= num_skip;
896 curr_frame = (curr_frame < 0 ? 0 : curr_frame + 1);
897
898 retval = ovl (stk, curr_frame);
899 }
900
901 return retval;
902}
903
904// A function that can be easily called from a debugger print the Octave stack.
905// This can be useful for finding what line of code the interpreter is
906// currently executing when the debugger is stopped in some C++ function,
907// for example.
908
909void
911{
912 do_dbstack (octave::__get_interpreter__ (),
913 octave_value_list (), 0, std::cerr);
914}
915
916DEFMETHOD (dbstack, interp, args, nargout,
917 doc: /* -*- texinfo -*-
918@deftypefn {} {} dbstack
919@deftypefnx {} {} dbstack @var{n}
920@deftypefnx {} {} dbstack -completenames
921@deftypefnx {} {[@var{stack}, @var{idx}] =} dbstack (@dots{})
922Display or return current debugging function stack information.
923
924With optional argument @var{n}, omit the @var{n} innermost stack frames.
925
926Although accepted, the argument @option{-completenames} is silently ignored.
927Octave always returns absolute filenames.
928
929The arguments @var{n} and @option{-completenames} can both be specified and may
930appear in any order.
931
932The optional return argument @var{stack} is a struct array with the
933following fields:
934
935@table @asis
936@item file
937The name of the m-file where the function code is located.
938
939@item name
940The name of the function with a breakpoint.
941
942@item line
943The line number of an active breakpoint.
944
945@item column
946The column number of the line where the breakpoint begins.
947
948@end table
949
950The return argument @var{idx} specifies which element of the @var{stack}
951struct array is currently active.
952@seealso{dbup, dbdown, dbwhere, dblist, dbstatus}
953@end deftypefn */)
954{
955 return do_dbstack (interp, args, nargout, octave_stdout);
956}
957
958static void
959do_dbupdown (octave::interpreter& interp, const octave_value_list& args,
960 const std::string& who)
961{
962 int n = 1;
963
964 if (args.length () == 1)
965 {
966 octave_value arg = args(0);
967
968 if (arg.is_string ())
969 n = parse_integer_argument (arg.string_value (), who.c_str ());
970 else
971 n = args(0).int_value ();
972 }
973
974 if (who == "dbup")
975 n = -n;
976
977 octave::tree_evaluator& tw = interp.get_evaluator ();
978
979 tw.dbupdown (n, true);
980}
981
982DEFMETHOD (dbup, interp, args, ,
983 doc: /* -*- texinfo -*-
984@deftypefn {} {} dbup
985@deftypefnx {} {} dbup @var{n}
986In debugging mode, move up the execution stack @var{n} frames.
987
988If @var{n} is omitted, move up one frame.
989@seealso{dbstack, dbdown}
990@end deftypefn */)
991{
992 do_dbupdown (interp, args, "dbup");
993
994 return ovl ();
995}
996
997DEFMETHOD (dbdown, interp, args, ,
998 doc: /* -*- texinfo -*-
999@deftypefn {} {} dbdown
1000@deftypefnx {} {} dbdown @var{n}
1001In debugging mode, move down the execution stack @var{n} frames.
1002
1003If @var{n} is omitted, move down one frame.
1004@seealso{dbstack, dbup}
1005@end deftypefn */)
1006{
1007 do_dbupdown (interp, args, "dbdown");
1008
1009 return ovl ();
1010}
1011
1012DEFMETHOD (dbstep, interp, args, ,
1013 doc: /* -*- texinfo -*-
1014@deftypefn {} {} dbstep
1015@deftypefnx {} {} dbstep @var{n}
1016@deftypefnx {} {} dbstep in
1017@deftypefnx {} {} dbstep out
1018@deftypefnx {} {} dbnext @dots{}
1019In debugging mode, execute the next @var{n} lines of code.
1020
1021If @var{n} is omitted, execute the next single line of code. If the next
1022line of code is itself defined in terms of an m-file remain in the existing
1023function.
1024
1025Using @code{dbstep in} will cause execution of the next line to step into
1026any m-files defined on the next line.
1027
1028Using @code{dbstep out} will cause execution to continue until the current
1029function returns.
1030
1031Programming Note: @code{dbnext} is an alias for @code{dbstep} and can be used
1032interchangeably.
1033@seealso{dbcont, dbquit}
1034@end deftypefn */)
1035{
1036 octave::tree_evaluator& tw = interp.get_evaluator ();
1037
1038 if (! tw.in_debug_repl ())
1039 error ("dbstep: can only be called in debug mode");
1040
1041 int nargin = args.length ();
1042
1043 if (nargin > 1)
1044 print_usage ();
1045
1046 int n = 0;
1047
1048 if (nargin == 1)
1049 {
1050 std::string arg
1051 = args(0).xstring_value ("dbstep: input argument must be a string");
1052
1053 if (arg == "in")
1054 n = -1;
1055 else if (arg == "out")
1056 n = -2;
1057 else
1058 {
1059 n = parse_integer_argument (arg, "dbstep");
1060
1061 if (n < 1)
1062 error ("dbstep: N must be greater than zero");
1063 }
1064 }
1065 else
1066 n = 1;
1067
1068 if (n != 0)
1069 {
1070 tw.set_dbstep_flag (n);
1071
1072 // If we set the dbstep flag, we also need to reset debug_mode.
1073 tw.reset_debug_state ();
1074
1075 }
1076
1077 return ovl ();
1078}
1079
1080DEFALIAS (dbnext, dbstep);
1081
1082DEFMETHOD (dbcont, interp, args, ,
1083 doc: /* -*- texinfo -*-
1084@deftypefn {} {} dbcont
1085Leave command-line debugging mode and continue code execution normally.
1086@seealso{dbstep, dbquit}
1087@end deftypefn */)
1088{
1089 octave::tree_evaluator& tw = interp.get_evaluator ();
1090
1091 if (! tw.in_debug_repl ())
1092 error ("dbcont: can only be called in debug mode");
1093
1094 if (args.length () != 0)
1095 print_usage ();
1096
1097 tw.dbcont ();
1098
1099 return ovl ();
1100}
1101
1102DEFMETHOD (dbquit, interp, args, ,
1103 doc: /* -*- texinfo -*-
1104@deftypefn {} {} dbquit
1105@deftypefnx {} {} dbquit all
1106Quit debugging mode immediately without further code execution.
1107
1108With no arguments, exit the current debugging level. With argument @code{all},
1109exit all debugging levels and return to the Octave prompt.
1110@seealso{dbcont, dbstep}
1111@end deftypefn */)
1112{
1113 octave::tree_evaluator& tw = interp.get_evaluator ();
1114
1115 if (! tw.in_debug_repl ())
1116 error ("dbquit: can only be called in debug mode");
1117
1118 int nargin = args.length ();
1119
1120 if (nargin > 1)
1121 print_usage ();
1122
1123 if (nargin == 1)
1124 {
1125 std::string arg
1126 = args(0).xstring_value ("dbquit: input argument must be a string");
1127
1128 if (arg == "all")
1129 tw.dbquit (true);
1130 else
1131 error ("dbquit: unrecognized argument '%s'", arg.c_str ());
1132 }
1133 else
1134 tw.dbquit ();
1135
1136 return ovl ();
1137}
1138
1139DEFMETHOD (isdebugmode, interp, args, ,
1140 doc: /* -*- texinfo -*-
1141@deftypefn {} {@var{tf} =} isdebugmode ()
1142Return true if in debugging mode, otherwise false.
1143@seealso{dbwhere, dbstack, dbstatus}
1144@end deftypefn */)
1145{
1146 if (args.length () != 0)
1147 print_usage ();
1148
1149 octave::tree_evaluator& tw = interp.get_evaluator ();
1150
1151 return ovl (tw.in_debug_repl ());
1152}
1153
1154DEFMETHOD (__db_next_breakpoint_quiet__, interp, args, ,
1155 doc: /* -*- texinfo -*-
1156@deftypefn {} {} __db_next_breakpoint_quiet__ ()
1157@deftypefnx {} {} __db_next_breakpoint_quiet__ (@var{flag})
1158Disable line info printing at the next breakpoint.
1159
1160With a logical argument @var{flag}, set the state on or off.
1161@end deftypefn */)
1162{
1163 int nargin = args.length ();
1164
1165 if (nargin > 1)
1166 print_usage ();
1167
1168 bool state = true;
1169
1170 if (nargin == 1)
1171 state = args(0).bool_value ();
1172
1173 octave::tree_evaluator& tw = interp.get_evaluator ();
1174
1175 tw.quiet_breakpoint_flag (state);
1176
1177 return ovl ();
1178}
1179
1180OCTAVE_END_NAMESPACE(octave)
void clear()
octave_idx_type numel() const
Number of elements in the array.
Definition Array-base.h:440
Definition Cell.h:41
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:92
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:810
bool is_string() const
Definition ov.h:635
bool isempty() const
Definition ov.h:599
bool iscell() const
Definition ov.h:602
std::string string_value(bool force=false) const
Definition ov.h:981
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void show_octave_dbstack()
Definition debug.cc:910
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:1008
void err_wrong_type_arg(const char *name, const char *s)
Definition errwarn.cc:166
T::size_type numel(const T &str)
Definition oct-string.cc:81
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
F77_RET_T const F77_DBLE const F77_DBLE * f