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