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