GNU Octave 7.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
bp-table.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 <algorithm>
31#include <limits>
32#include <list>
33#include <map>
34#include <set>
35#include <string>
36
37#include "file-ops.h"
38#include "oct-env.h"
39
40#include "bp-table.h"
41#include "defun-int.h"
42#include "error.h"
43#include "event-manager.h"
44#include "interpreter.h"
45#include "interpreter-private.h"
46#include "oct-map.h"
47#include "ov-usr-fcn.h"
48#include "ov.h"
49#include "ovl.h"
50#include "pager.h"
51#include "parse.h"
52#include "pt-eval.h"
53#include "pt-exp.h"
54#include "pt-stmt.h"
55#include "sighandlers.h"
56
57namespace octave
58{
60 {
61 public:
62
63 bp_file_info (tree_evaluator& tw, const std::string& file)
64 : m_ok (false), m_file (file), m_dir (), m_fcn (), m_class_name ()
65 {
66 std::string abs_file = sys::env::make_absolute (file);
67
68 std::string dir = sys::file_ops::dirname (abs_file);
69 std::string fcn = sys::file_ops::tail (abs_file);
70 std::size_t len = fcn.length ();
71 if (len >= 2 && fcn[len-2] == '.' && fcn[len-1] == 'm')
72 fcn = fcn.substr (0, len-2);
73
74 std::size_t pos = dir.rfind (sys::file_ops::dir_sep_chars ());
75
76 if (pos != std::string::npos && pos < dir.length () - 1)
77 {
78 if (dir[pos+1] == '@')
79 {
80 m_class_name = dir.substr (pos+1);
81
83
84 dir = dir.substr (0, pos);
85 }
86 }
87
88 m_dir = dir;
89 m_fcn = fcn;
90
91 interpreter& interp = tw.get_interpreter ();
92
93 load_path& lp = interp.get_load_path ();
94
96 m_ok = true;
97 }
98
99 std::string file (void) const { return m_file; }
100 std::string dir (void) const { return m_fcn; }
101 std::string fcn (void) const { return m_fcn; }
102 std::string class_name (void) const { return m_class_name; }
103
104 bool ok (void) const { return m_ok; }
105
106 private:
107
108 bool m_ok;
109 std::string m_file;
110 std::string m_dir;
111 std::string m_fcn;
112 std::string m_class_name;
113 };
114
115 // Clear all reasons to stop, other than breakpoints.
116
118 {
120 error_system& es = interp.get_error_system ();
121
122 es.debug_on_error (false);
124
125 es.debug_on_caught (false);
127
128 es.debug_on_warning (false);
130
131 Vdebug_on_interrupt = false;
132 }
133
134 // Process the "warn", "errs", "caught" and "intr" fields for a call of
135 // "dbstop (p)".
136
138 {
140 error_system& es = interp.get_error_system ();
141
142 // process errs
143 // why so many levels of indirection needed?
144 bool fail = false;
145 Cell U = mv.contents ("errs");
146 if (U.numel () != 1)
147 fail = (U.numel () > 1);
148 else
149 {
150 Array<octave_value> W = U.index (0);
151 if (W.isempty () || W(0).isempty ())
152 es.debug_on_error (true); // like "dbstop if error" with no identifier
153 else if (! W(0).iscell ())
154 fail = true;
155 else
156 {
157 Cell V = W(0).cell_value ();
158 for (int i = 0; i < V.numel (); i++)
159 {
160 m_errors_that_stop.insert (V(i).string_value ());
161 es.debug_on_error (true);
162 }
163 }
164 }
165
166 if (fail)
167 error ("dbstop: invalid 'errs' field");
168
169 // process caught
170 // why so many levels of indirection needed?
171 fail = false;
172 U = mv.contents ("caught");
173 if (U.numel () != 1)
174 fail = (U.numel () > 1);
175 else
176 {
177 Array<octave_value> W = U.index (0);
178 if (W.isempty () || W(0).isempty ())
179 es.debug_on_caught (true); // like "dbstop if caught error" with no ID
180 else if (! W(0).iscell ())
181 fail = true;
182 else
183 {
184 Cell V = W(0).cell_value ();
185 for (int i = 0; i < V.numel (); i++)
186 {
187 m_caught_that_stop.insert (V(i).string_value ());
188 es.debug_on_caught (true);
189 }
190 }
191 }
192
193 if (fail)
194 error ("dbstop: invalid 'caught' field");
195
196 // process warn
197 // why so many levels of indirection needed?
198 fail = false;
199 U = mv.contents ("warn");
200 if (U.numel () != 1)
201 fail = (U.numel () > 1);
202 else
203 {
204 Array<octave_value> W = U.index (0);
205 if (W.isempty () || W(0).isempty ())
206 es.debug_on_warning (true); // like "dbstop if warning" with no identifier
207 else if (! W(0).iscell ())
208 fail = true;
209 else
210 {
211 Cell V = W(0).cell_value ();
212 for (int i = 0; i < V.numel (); i++)
213 {
214 m_warnings_that_stop.insert (V(i).string_value ());
215 es.debug_on_warning (true);
216 }
217 }
218 }
219
220 if (fail)
221 error ("dbstop: invalid 'warn' field");
222
223 // process interrupt
224 if (mv.isfield ("intr"))
225 Vdebug_on_interrupt = true;
226 }
227
228 // Insert a breakpoint in function fcn at line within file fname,
229 // to stop only when condition is true.
230 // Record in m_bp_set that fname contains a breakpoint.
231
233 const std::string& fname,
234 const bp_table::bp_lines& line,
235 const std::string& condition,
236 bp_table::bp_lines& retval)
237 {
238 bool found = false;
239
240 tree_statement_list *cmds = fcn->body ();
241
242 std::string file = fcn->fcn_file_name ();
243
244 if (cmds)
245 {
247
248 event_manager& evmgr = interp.get_event_manager ();
249
250 retval = cmds->add_breakpoint (evmgr, file, line, condition);
251
252 for (auto& lineno : retval)
253 {
254 if (lineno != 0)
255 {
256 // Normalize to store only the file name.
257 // Otherwise, there can be an entry for both
258 // file>subfunction and file, which causes a crash on
259 // dbclear all
260 const char *s = strchr (fname.c_str (), '>');
261 if (s)
262 m_bp_set.insert (fname.substr (0, s - fname.c_str ()));
263 else
264 m_bp_set.insert (fname);
265 found = true;
266 break;
267 }
268 }
269 }
270
271 return found;
272 }
273
274 // Cursory check that cond is a valid condition to use for a breakpoint.
275 // Currently allows conditions with side-effects, like 'y+=10' and 'y++';
276 // it is odd that the former is not flagged by "is_assignment_expression".
277 // Throws an exception if not valid.
278
279 bool bp_table::condition_valid (const std::string& cond)
280 {
281 if (cond.length () > 0)
282 {
283 // ; to reject partial expr like "y=="
284 parser parser (cond + " ;", m_evaluator.get_interpreter ());
285 parser.reset ();
286 int parse_status = parser.run ();
287 if (parse_status)
288 error ("dbstop: Cannot parse condition '%s'", cond.c_str ());
289 else
290 {
291 tree_statement *stmt = nullptr;
292
293 std::shared_ptr<tree_statement_list> stmt_list
295
296 if (! stmt_list)
297 error ("dbstop: "
298 "condition is not empty, but has nothing to evaluate");
299 else
300 {
301 if (stmt_list->length () == 1
302 && (stmt = stmt_list->front ())
303 && stmt->is_expression ())
304 {
305 tree_expression *expr = stmt->expression ();
306 if (expr->is_assignment_expression ())
307 error ("dbstop: condition cannot be an assignment. "
308 "Did you mean '=='?");
309 }
310 else
311 error ("dbstop: condition must be an expression");
312 }
313 }
314 }
315
316 return true;
317 }
318
320 {
325 };
326
327 // FIXME: This function probably needs to be completely overhauled to
328 // correctly parse the full syntax of the dbstop command and properly
329 // reject incorrect forms.
330
331 // Parse parameters (args) of dbstop and dbclear commands.
332 // For dbstop, who=="dbstop"; for dbclear, who=="dbclear".
333 // The syntax is: dbstop [[in] symbol] [[at] [method | line [line [...]]]] [if condition]
334 // where the form of condition depends on whether or not a file or line has
335 // been seen. IF symbol and method are specified, then symbol should
336 // be a class name. Otherwise it should be a function name.
337 // Also execute "if [error|warning|interrupt|naninf]" clauses.
338
340 const octave_value_list& args,
341 std::string& func_name,
342 std::string& class_name,
343 bp_table::bp_lines& lines,
344 std::string& cond)
345 {
346 int nargin = args.length ();
347 func_name = "";
348 class_name = "";
349 lines = bp_table::bp_lines ();
350
351 if (nargin == 0 || ! args(0).is_string ())
352 print_usage (who);
353
354 // elements already processed
355 bool seen_in = false;
356 bool seen_at = false;
357 bool seen_if = false;
358 int pos = 0;
360 while (pos < nargin)
361 {
362 // allow "in" and "at" to be implicit
363 if (args(pos).is_string ())
364 {
365 std::string arg = args(pos).string_value ();
366 if (arg == "in")
367 {
368 tok = dbstop_in;
369 pos++;
370 }
371 else if (arg == "at")
372 {
373 tok = dbstop_at;
374 pos++;
375 }
376 else if (arg == "if")
377 {
378 tok = dbstop_if;
379 pos++;
380 }
381 else if (atoi (args(pos).string_value ().c_str ()) > 0)
382 tok = dbstop_at;
383 else
384 tok = dbstop_in;
385 }
386 else
387 tok = dbstop_at;
388
389 if (pos >= nargin)
390 error ("%s: '%s' missing argument", who,
391 (tok == dbstop_in
392 ? "in" : (tok == dbstop_at ? "at" : "if")));
393
394 // process the actual arguments
395 switch (tok)
396 {
397 case dbstop_in:
398 func_name = args(pos).string_value ();
399 if (seen_in)
400 error ("%s: Too many function names specified -- %s",
401 who, func_name.c_str ());
402 else if (seen_at || seen_if)
403 error ("%s: function name must come before line number and 'if'",
404 who);
405 seen_in = true;
406 pos++;
407 break;
408
409 case dbstop_at:
410 if (seen_at)
411 error ("%s: Only one 'at' clause is allowed -- %s",
412 who, args(pos).string_value ().c_str ());
413 else if (seen_if)
414 error ("%s: line number must come before 'if' clause\n", who);
415 seen_at = true;
416
417 if (seen_if)
418 error ("%s: line number must come before 'if' clause\n", who);
419 else if (seen_in)
420 {
421 std::string arg = args(pos).string_value ();
422
423 // FIXME: we really want to distinguish number
424 // vs. method name here.
425
426 if (atoi (arg.c_str ()) == 0)
427 {
428 // We have class and function names but already
429 // stored the class name in func_name.
430 class_name = func_name;
431 func_name = arg;
432 pos++;
433 break;
434 }
435
436 }
437 else
438 {
439 // It was a line number. Get function name from debugger.
441 func_name = m_evaluator.get_user_code ()->profiler_name ();
442 else
443 error ("%s: function name must come before line number "
444 "and 'if'", who);
445 seen_in = true;
446 }
447
448 // Read a list of line numbers (or arrays thereof)
449 for ( ; pos < nargin; pos++)
450 {
451 if (args(pos).is_string ())
452 {
453 int line = atoi (args(pos).string_value ().c_str ());
454
455 if (line > 0)
456 lines.insert (line);
457 else
458 break; // may be "if" or a method name
459 }
460 else if (args(pos).isnumeric ())
461 {
462 const NDArray arg = args(pos).array_value ();
463
464 for (octave_idx_type j = 0; j < arg.numel (); j++)
465 lines.insert (static_cast<int> (arg.elem (j)));
466 }
467 else
468 error ("%s: Invalid argument type %s",
469 who, args(pos).type_name ().c_str ());
470 }
471 break;
472
473 case dbstop_if:
474 if (seen_in) // conditional breakpoint
475 {
476 cond = ""; // remaining arguments form condition
477 for (; pos < nargin; pos++)
478 {
479 if (args(pos).is_string ())
480 cond += ' ' + args(pos).string_value ();
481 else
482 error ("%s: arguments to 'if' must all be strings", who);
483 }
484
485 cond = cond.substr (1); // omit initial space
486 }
487 else // stop on event (error, warning, interrupt, NaN/inf)
488 {
489 std::string condition = args(pos).string_value ();
490 bool on_off = ! strcmp (who, "dbstop");
491
492 // FIXME: the following seems a bit messy in the way it
493 // duplicates checks on CONDITION.
494
495 if (condition == "error")
496 process_id_list (who, condition, args, nargin, pos, on_off,
498 else if (condition == "warning")
499 process_id_list (who, condition, args, nargin, pos, on_off,
501 else if (condition == "caught" && nargin > pos+1
502 && args(pos+1).string_value () == "error")
503 {
504 pos++;
505 process_id_list (who, condition, args, nargin, pos, on_off,
507 }
508 else if (condition == "interrupt")
509 {
510 Vdebug_on_interrupt = on_off;
511 }
512 else if (condition == "naninf")
513 {
514#if defined (DBSTOP_NANINF)
515 Vdebug_on_naninf = on_off;
516 enable_fpe (on_off);
517#else
518 warning ("%s: condition '%s' not yet supported",
519 who, condition.c_str ());
520#endif
521 }
522 else
523 error ("%s: invalid condition %s",
524 who, condition.c_str ());
525
526 pos = nargin;
527 }
528 break;
529
530 default: // dbstop_none should never occur
531 break;
532 }
533 }
534 }
535
536/*
537%!test
538%! dbclear all; # Clear out breakpoints before test
539%! dbstop help;
540%! dbstop in ls;
541%! dbstop help at 104;
542%! dbstop in ls 102; ## 102 is a comment; code line is at 105
543%! dbstop help 204 if a==5;
544%! dbstop if error Octave:undefined-function;
545%! s = dbstatus;
546%! dbclear all;
547%! assert ({s.bkpt(:).name}, {"help", "help", "help>do_contents", "ls", "ls"});
548%! assert ([s.bkpt(:).line], [55, 105, 207, 63, 102]);
549%! assert (s.errs, {"Octave:undefined-function"});
550*/
551
552 void bp_table::set_stop_flag (const char *who, const std::string& condition,
553 bool on_off)
554 {
556 error_system& es = interp.get_error_system ();
557
558 if (condition == "error")
559 es.debug_on_error (on_off);
560 else if (condition == "warning")
561 es.debug_on_warning (on_off);
562 else if (condition == "caught")
563 es.debug_on_caught (on_off);
564 else
565 error ("%s: internal error in set_stop_flag", who);
566 }
567
568 void bp_table::process_id_list (const char *who,
569 const std::string& condition,
570 const octave_value_list& args,
571 int nargin, int& pos, bool on_off,
572 std::set<std::string>& id_list)
573 {
574 pos++;
575
576 if (nargin > pos) // only affect a single error ID
577 {
578 if (! args(pos).is_string () || nargin > pos+1)
579 error ("%s: ID must be a single string", who);
580 else if (on_off)
581 {
582 id_list.insert (args(pos).string_value ());
583 set_stop_flag (who, condition, true);
584 }
585 else
586 {
587 id_list.erase (args(pos).string_value ());
588 if (id_list.empty ())
589 set_stop_flag (who, condition, false);
590 }
591 }
592 else // unqualified. Turn all on or off
593 {
594 id_list.clear ();
595 set_stop_flag (who, condition, on_off);
596
597 if (condition == "error")
598 {
599 // Matlab stops on both.
600 Vdebug_on_interrupt = on_off;
601 }
602 }
603 }
604
605 // Return the sub/nested/main function of MAIN_FCN that contains
606 // line number LINENO of the source file.
607 // If END_LINE != 0, *END_LINE is set to last line of the returned function.
608
610 int lineno,
611 int *end_line = nullptr)
612 {
613 octave_user_code *retval = nullptr;
614 octave_user_code *next_fcn = nullptr; // 1st function starting after lineno
615
616 // Find innermost nested (or parent) function containing lineno.
617 int earliest_end = std::numeric_limits<int>::max ();
618
619 std::map<std::string, octave_value> subfcns = main_fcn->subfunctions ();
620 for (const auto& str_val_p : subfcns)
621 {
622 if (str_val_p.second.is_user_function ())
623 {
624 auto *dbg_subfcn = str_val_p.second.user_function_value ();
625
626 // Check if lineno is within dbg_subfcn.
627 // FIXME: we could break when beginning_line() > lineno,
628 // but that makes the code "fragile"
629 // if the order of walking subfcns changes,
630 // for a minor speed improvement in non-critical code.
631 if (dbg_subfcn->ending_line () < earliest_end
632 && dbg_subfcn->ending_line () >= lineno
633 && dbg_subfcn->beginning_line () <= lineno)
634 {
635 earliest_end = dbg_subfcn->ending_line ();
636 retval = find_fcn_by_line (dbg_subfcn, lineno, &earliest_end);
637 }
638
639 // Find the first fcn starting after lineno.
640 // This is used if line is not inside any function.
641 if (dbg_subfcn->beginning_line () >= lineno && ! next_fcn)
642 next_fcn = dbg_subfcn;
643 }
644 }
645
646 // The breakpoint is either in the subfunction found above,
647 // or in the main function, which we check now.
648 if (main_fcn->is_user_function ())
649 {
650 int e = dynamic_cast<octave_user_function *> (main_fcn)->ending_line ();
651 if (e >= lineno && e < earliest_end)
652 retval = main_fcn;
653
654 if (! retval)
655 retval = next_fcn;
656 }
657 else // main_fcn is a script.
658 {
659 if (! retval)
660 retval = main_fcn;
661 }
662
663 if (end_line && earliest_end < *end_line)
664 *end_line = earliest_end;
665
666 return retval;
667 }
668
669 // Given file name fname, find the subfunction at line and create
670 // a breakpoint there. Put the system into debug_mode.
671 int bp_table::add_breakpoint_in_function (const std::string& fname,
672 const std::string& class_name,
673 int line, const std::string& condition)
674 {
675 bp_lines line_info;
676 line_info.insert (line);
677
678 bp_lines result
679 = add_breakpoints_in_function (fname, class_name, line_info, condition);
680
681 return result.empty () ? 0 : *(result.begin ());
682 }
683
684 // Given file name fname, find the subfunction at line and create
685 // a breakpoint there. Put the system into debug_mode.
687 bp_table::add_breakpoints_in_function (const std::string& fname,
688 const std::string& class_name,
689 const bp_table::bp_lines& lines,
690 const std::string& condition)
691 {
692 octave_user_code *main_fcn = m_evaluator.get_user_code (fname, class_name);
693
694 if (! main_fcn)
695 error ("add_breakpoints_in_function: unable to find function '%s'\n",
696 fname.c_str ());
697
698 condition_valid (condition); // Throw error if condition not valid.
699
700 bp_lines retval;
701
702 for (const auto& lineno : lines)
703 {
704 octave_user_code *dbg_fcn = find_fcn_by_line (main_fcn, lineno);
705
706 // We've found the right (sub)function. Now insert the breakpoint.
707 bp_lines line_info;
708 line_info.insert (lineno);
709
710 bp_lines ret_one;
711 if (dbg_fcn && add_breakpoint_1 (dbg_fcn, fname, line_info,
712 condition, ret_one))
713 {
714 if (! ret_one.empty ())
715 {
716 int line = *(ret_one.begin ());
717
718 if (line)
719 retval.insert (line);
720 }
721 }
722 }
723
725
726 return retval;
727 }
728
729 int bp_table::add_breakpoint_in_file (const std::string& file,
730 int line,
731 const std::string& condition)
732 {
733 // Duplicates what the GUI was doing previously, but this action
734 // should not be specific to the GUI.
735
736 bp_file_info info (m_evaluator, file);
737
738 if (! info.ok ())
739 return 0;
740
741 return add_breakpoint_in_function (info.fcn (), info.class_name (),
742 line, condition);
743 }
744
746 bp_table::add_breakpoints_in_file (const std::string& file,
747 const bp_lines& lines,
748 const std::string& condition)
749 {
750 // Duplicates what the GUI was doing previously, but this action
751 // should not be specific to the GUI.
752
753 bp_file_info info (m_evaluator, file);
754
755 if (! info.ok ())
756 return bp_lines ();
757
758 return add_breakpoints_in_function (info.fcn (), info.class_name (),
759 lines, condition);
760 }
761
763 const std::string& fname,
764 const bp_table::bp_lines& lines)
765 {
766 int retval = 0;
767
768 std::string file = fcn->fcn_file_name ();
769
770 tree_statement_list *cmds = fcn->body ();
771
772 // FIXME: move the operation on cmds to the tree_statement_list class?
773
774 if (cmds)
775 {
776 octave_value_list results = cmds->list_breakpoints ();
777
778 if (results.length () > 0)
779 {
781
782 event_manager& evmgr = interp.get_event_manager ();
783
784 for (const auto& lineno : lines)
785 {
786 cmds->delete_breakpoint (lineno);
787
788 if (! file.empty ())
789 evmgr.update_breakpoint (false, file, lineno);
790 }
791
792 results = cmds->list_breakpoints ();
793
794 auto it = m_bp_set.find (fname);
795 if (results.empty () && it != m_bp_set.end ())
796 m_bp_set.erase (it);
797 }
798
799 retval = results.length ();
800 }
801
802 return retval;
803 }
804
805 int
806 bp_table::remove_breakpoint_from_function (const std::string& fname, int line)
807 {
808 bp_lines line_info;
809 line_info.insert (line);
810
811 return remove_breakpoints_from_function (fname, line_info);
812 }
813
814 int
816 const bp_table::bp_lines& lines)
817 {
818 int retval = 0;
819
820 if (lines.empty ())
821 {
823 retval = results.size ();
824 }
825 else
826 {
827 octave_user_code *dbg_fcn = m_evaluator.get_user_code (fname);
828
829 if (! dbg_fcn)
830 error ("remove_breakpoints_from_function: unable to find function %s\n",
831 fname.c_str ());
832
833 retval = remove_breakpoint_1 (dbg_fcn, fname, lines);
834
835 // Search subfunctions in the order they appear in the file.
836
837 const std::list<std::string> subfcn_names
838 = dbg_fcn->subfunction_names ();
839
840 std::map<std::string, octave_value> subfcns
841 = dbg_fcn->subfunctions ();
842
843 for (const auto& subf_nm : subfcn_names)
844 {
845 const auto q = subfcns.find (subf_nm);
846
847 if (q != subfcns.end ())
848 {
849 octave_user_code *dbg_subfcn = q->second.user_code_value ();
850
851 retval += remove_breakpoint_1 (dbg_subfcn, fname, lines);
852 }
853 }
854 }
855
857
858 return retval;
859 }
860
861 // Remove all breakpoints from a file, including those in subfunctions.
862
865 bool silent)
866 {
867 bp_lines retval;
868
869 octave_user_code *dbg_fcn = m_evaluator.get_user_code (fname);
870
871 if (dbg_fcn)
872 {
873 std::string file = dbg_fcn->fcn_file_name ();
874
875 tree_statement_list *cmds = dbg_fcn->body ();
876
877 if (cmds)
878 {
880
881 event_manager& evmgr = interp.get_event_manager ();
882
883 retval = cmds->remove_all_breakpoints (evmgr, file);
884
885 auto it = m_bp_set.find (fname);
886 if (it != m_bp_set.end ())
887 m_bp_set.erase (it);
888 }
889 }
890 else if (! silent)
891 error ("remove_all_breakpoints_from_function: "
892 "unable to find function %s\n", fname.c_str ());
893
895
896 return retval;
897 }
898
899 int
900 bp_table::remove_breakpoint_from_file (const std::string& file, int line)
901 {
902 // Duplicates what the GUI was doing previously, but this action
903 // should not be specific to the GUI.
904
905 bp_file_info info (m_evaluator, file);
906
907 if (! info.ok ())
908 return 0;
909
910 return remove_breakpoint_from_function (info.fcn (), line);
911 }
912
913 int
915 const bp_lines& lines)
916 {
917 // Duplicates what the GUI was doing previously, but this action
918 // should not be specific to the GUI.
919
920 bp_file_info info (m_evaluator, file);
921
922 if (! info.ok ())
923 return 0;
924
925 return remove_breakpoints_from_function (info.fcn (), lines);
926 }
927
930 bool silent)
931 {
932 // Duplicates what the GUI was doing previously, but this action
933 // should not be specific to the GUI.
934
935 bp_file_info info (m_evaluator, file);
936
937 if (! info.ok ())
938 return bp_lines ();
939
940 return remove_all_breakpoints_from_function (info.fcn (), silent);
941 }
942
944 {
945 // Odd loop structure required because delete will invalidate
946 // m_bp_set iterators.
947 for (auto it = m_bp_set.cbegin (), it_next = it;
948 it != m_bp_set.cend ();
949 it = it_next)
950 {
951 ++it_next;
953 }
954
956 }
957
958 std::string find_bkpt_list (octave_value_list slist, std::string match)
959 {
960 std::string retval;
961
962 for (int i = 0; i < slist.length (); i++)
963 {
964 if (slist(i).string_value () == match)
965 {
966 retval = slist(i).string_value ();
967 break;
968 }
969 }
970
971 return retval;
972 }
973
976 {
977 fname_bp_map retval;
978
979 // make copy since changes may invalidate iters of m_bp_set.
980 std::set<std::string> tmp_bp_set = m_bp_set;
981
982 for (auto& bp_fname : tmp_bp_set)
983 {
984 if (fname_list.empty ()
985 || find_bkpt_list (fname_list, bp_fname) != "")
986 {
987 octave_user_code *dbg_fcn = m_evaluator.get_user_code (bp_fname);
988
989 if (dbg_fcn)
990 {
991 tree_statement_list *cmds = dbg_fcn->body ();
992
993 // FIXME: move the operation on cmds to the
994 // tree_statement_list class?
995 if (cmds)
996 {
997 std::list<bp_type> bkpts = cmds->breakpoints_and_conds ();
998
999 if (! bkpts.empty ())
1000 retval[bp_fname] = bkpts;
1001 }
1002
1003 // look for breakpoints in subfunctions
1004 const std::list<std::string> subf_nm
1005 = dbg_fcn->subfunction_names ();
1006
1007 std::map<std::string, octave_value> subfcns
1008 = dbg_fcn->subfunctions ();
1009
1010 for (const auto& subfcn_nm : subf_nm)
1011 {
1012 const auto q = subfcns.find (subfcn_nm);
1013
1014 if (q != subfcns.end ())
1015 {
1016 octave_user_code *dbg_subfcn
1017 = q->second.user_code_value ();
1018
1019 cmds = dbg_subfcn->body ();
1020 if (cmds)
1021 {
1022 std::list<bp_type> bkpts
1023 = cmds->breakpoints_and_conds ();
1024
1025 if (! bkpts.empty ())
1026 {
1027 std::string key
1028 = bp_fname + '>' + dbg_subfcn->name ();
1029
1030 retval[key] = bkpts;
1031 }
1032 }
1033 }
1034 }
1035 }
1036 }
1037 }
1038
1039 return retval;
1040 }
1041
1042 // Report the status of "dbstop if error ..." and "dbstop if warning ..."
1043 // If to_screen is true, the output goes to octave_stdout; otherwise it is
1044 // returned.
1045 // If dbstop if error is true but no explicit IDs are specified, the return
1046 // value will have an empty field called "errs". If IDs are specified, the
1047 // "errs" field will have a row per ID. If dbstop if error is false, there
1048 // is no "errs" field. The "warn" field is set similarly by dbstop if warning
1049
1051 {
1052 octave_map retval;
1053
1055 error_system& es = interp.get_error_system ();
1056
1057 // print dbstop if error information
1058 if (es.debug_on_error ())
1059 {
1060 if (m_errors_that_stop.empty ())
1061 {
1062 if (to_screen)
1063 octave_stdout << "stop if error\n";
1064 else
1065 retval.assign ("errs", octave_value (""));
1066 }
1067 else
1068 {
1069 Cell errs (dim_vector (bp_table::m_errors_that_stop.size (), 1));
1070 int i = 0;
1071
1072 for (const auto& e : m_errors_that_stop)
1073 {
1074 if (to_screen)
1075 octave_stdout << "stop if error " << e << "\n";
1076 else
1077 errs(i++) = e;
1078 }
1079 if (! to_screen)
1080 retval.assign ("errs", octave_value (errs));
1081 }
1082 }
1083
1084 // print dbstop if caught error information
1085 if (es.debug_on_caught ())
1086 {
1087 if (m_caught_that_stop.empty ())
1088 {
1089 if (to_screen)
1090 octave_stdout << "stop if caught error\n";
1091 else
1092 retval.assign ("caught", octave_value (""));
1093 }
1094 else
1095 {
1096 Cell errs (dim_vector (m_caught_that_stop.size (), 1));
1097 int i = 0;
1098
1099 for (const auto& e : m_caught_that_stop)
1100 {
1101 if (to_screen)
1102 octave_stdout << "stop if caught error " << e << "\n";
1103 else
1104 errs(i++) = e;
1105 }
1106 if (! to_screen)
1107 retval.assign ("caught", octave_value (errs));
1108 }
1109 }
1110
1111 // print dbstop if warning information
1112 if (es.debug_on_warning ())
1113 {
1114 if (m_warnings_that_stop.empty ())
1115 {
1116 if (to_screen)
1117 octave_stdout << "stop if warning\n";
1118 else
1119 retval.assign ("warn", octave_value (""));
1120 }
1121 else
1122 {
1123 Cell warn (dim_vector (m_warnings_that_stop.size (), 1));
1124 int i = 0;
1125
1126 for (const auto& w : m_warnings_that_stop)
1127 {
1128 if (to_screen)
1129 octave_stdout << "stop if warning " << w << "\n";
1130 else
1131 warn(i++) = w;
1132 }
1133 if (! to_screen)
1134 retval.assign ("warn", octave_value (warn));
1135 }
1136 }
1137
1138 // print dbstop if interrupt information
1140 {
1141 if (to_screen)
1142 octave_stdout << "stop if interrupt\n";
1143 else
1144 retval.assign ("intr", octave_value (""));
1145 }
1146
1147 return retval;
1148 }
1149}
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:411
T & elem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:534
bool isempty(void) const
Size of the specified dimension.
Definition: Array.h:607
OCTARRAY_API Array< T, Alloc > & insert(const Array< T, Alloc > &a, const Array< octave_idx_type > &idx)
Insert an array into another at a specified position.
Definition: Array.cc:1588
Definition: Cell.h:43
Cell index(const octave_value_list &idx, bool resize_ok=false) const
Definition: Cell.cc:171
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
OCTINTERP_API octave_value debug_on_warning(const octave_value_list &args, int nargout)
Definition: error.cc:292
OCTINTERP_API octave_value debug_on_caught(const octave_value_list &args, int nargout)
Definition: error.cc:285
OCTINTERP_API octave_value debug_on_error(const octave_value_list &args, int nargout)
Definition: error.cc:278
Provides threadsafe access to octave.
void update_breakpoint(bool insert, const std::string &file, int line, const std::string &cond="")
error_system & get_error_system(void)
Definition: interpreter.h:251
load_path & get_load_path(void)
Definition: interpreter.h:281
event_manager & get_event_manager(void)
Definition: interpreter.h:328
bool contains_file_in_dir(const std::string &file_name, const std::string &dir_name)
Definition: load-path.cc:499
OCTINTERP_API void statement_list(std::shared_ptr< tree_statement_list > &lst)
Definition: oct-parse.cc:6813
OCTINTERP_API void reset(void)
Definition: oct-parse.cc:6711
std::string m_class_name
Definition: bp-table.cc:112
std::string fcn(void) const
Definition: bp-table.cc:101
bool ok(void) const
Definition: bp-table.cc:104
bp_file_info(tree_evaluator &tw, const std::string &file)
Definition: bp-table.cc:63
std::string class_name(void) const
Definition: bp-table.cc:102
std::string m_file
Definition: bp-table.cc:109
std::string dir(void) const
Definition: bp-table.cc:100
std::string m_fcn
Definition: bp-table.cc:111
std::string file(void) const
Definition: bp-table.cc:99
std::string m_dir
Definition: bp-table.cc:110
int remove_breakpoint_1(octave_user_code *fcn, const std::string &, const bp_lines &lines)
Definition: bp-table.cc:762
void process_id_list(const char *who, const std::string &condition, const octave_value_list &args, int nargin, int &pos, bool on_off, std::set< std::string > &id_list)
Definition: bp-table.cc:568
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
int remove_breakpoint_from_file(const std::string &file="", int line=1)
Definition: bp-table.cc:900
void dbclear_all_signals(void)
Definition: bp-table.cc:117
std::set< std::string > m_warnings_that_stop
Definition: bp-table.h:230
std::set< std::string > m_errors_that_stop
Definition: bp-table.h:228
bool add_breakpoint_1(octave_user_code *fcn, const std::string &fname, const bp_lines &line, const std::string &condition, bp_lines &retval)
Definition: bp-table.cc:232
int remove_breakpoints_from_file(const std::string &file="", const bp_lines &lines=bp_lines())
Definition: bp-table.cc:914
bp_lines add_breakpoints_in_file(const std::string &file="", const bp_lines &lines=bp_lines(), const std::string &condition="")
Definition: bp-table.cc:746
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
bool condition_valid(const std::string &cond)
Definition: bp-table.cc:279
int add_breakpoint_in_function(const std::string &fname="", const std::string &class_name="", int line=1, const std::string &condition="")
Definition: bp-table.cc:671
bp_lines remove_all_breakpoints_from_file(const std::string &file, bool silent=false)
Definition: bp-table.cc:929
int remove_breakpoint_from_function(const std::string &fname="", int line=1)
Definition: bp-table.cc:806
int add_breakpoint_in_file(const std::string &file="", int line=1, const std::string &condition="")
Definition: bp-table.cc:729
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< std::string > m_caught_that_stop
Definition: bp-table.h:229
bp_lines remove_all_breakpoints_from_function(const std::string &fname, bool silent=false)
Definition: bp-table.cc:864
std::set< std::string > m_bp_set
Definition: bp-table.h:223
void set_stop_flag(const char *who, const std::string &condition, bool on_off)
Definition: bp-table.cc:552
std::set< int > bp_lines
Definition: bp-table.h:68
tree_evaluator & m_evaluator
Definition: bp-table.h:220
OCTINTERP_API int run(void)
Definition: oct-parse.cc:9633
static std::string make_absolute(const std::string &s, const std::string &dot_path=get_current_directory())
Definition: oct-env.cc:131
virtual bool is_assignment_expression(void) const
Definition: pt-exp.h:74
void delete_breakpoint(int line)
Definition: pt-stmt.cc:199
octave_value_list list_breakpoints(void)
Definition: pt-stmt.cc:221
bp_table::bp_lines add_breakpoint(event_manager &evmgr, const std::string &file, const bp_table::bp_lines &lines, const std::string &condition)
Definition: pt-stmt.cc:255
std::list< bp_type > breakpoints_and_conds(void)
Definition: pt-stmt.cc:231
bp_table::bp_lines remove_all_breakpoints(event_manager &evmgr, const std::string &file)
Definition: pt-stmt.cc:279
bool is_expression(void) const
Definition: pt-stmt.h:80
tree_expression * expression(void)
Definition: pt-stmt.h:101
virtual bool is_user_function(void) const
Definition: ov-base.h:507
virtual octave_user_code * user_code_value(bool silent=false)
Definition: ov-base.cc:962
virtual std::string profiler_name(void) const
Definition: ov-fcn.h:83
virtual std::list< std::string > subfunction_names(void) const
Definition: ov-fcn.h:200
std::string name(void) const
Definition: ov-fcn.h:207
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 assign(const std::string &k, const Cell &val)
Definition: oct-map.h:365
std::string fcn_file_name(void) const
Definition: ov-usr-fcn.h:106
octave::tree_statement_list * body(void)
Definition: ov-usr-fcn.h:119
virtual std::map< std::string, octave_value > subfunctions(void) const
Definition: ov-usr-fcn.cc:132
Array< octave_value > array_value(void) const
Definition: ovl.h:90
bool empty(void) const
Definition: ovl.h:115
octave_idx_type length(void) const
Definition: ovl.h:113
void reset_debug_state(void)
Definition: pt-eval.cc:1362
interpreter & get_interpreter(void)
Definition: pt-eval.h:414
octave_user_code * get_user_code(const std::string &fname="", const std::string &class_name="")
Definition: pt-eval.cc:2864
bool in_debug_repl(void) const
Definition: pt-eval.cc:4843
OCTINTERP_API void print_usage(void)
Definition: defun-int.h:72
void warning(const char *fmt,...)
Definition: error.cc:1055
void error(const char *fmt,...)
Definition: error.cc:980
F77_RET_T const F77_INT const F77_INT const F77_INT const F77_DBLE const F77_DBLE F77_INT F77_DBLE * V
std::complex< double > w(std::complex< double > z, double relerr=0)
OCTAVE_API bool strcmp(const T &str_a, const T &str_b)
True if strings are the same.
std::string concat(const std::string &dir, const std::string &file)
Definition: file-ops.cc:349
std::string dirname(const std::string &path)
Definition: file-ops.cc:358
std::string dir_sep_chars(void)
Definition: file-ops.cc:247
std::string tail(const std::string &path)
Definition: file-ops.cc:365
static octave_user_code * find_fcn_by_line(octave_user_code *main_fcn, int lineno, int *end_line=nullptr)
Definition: bp-table.cc:609
dbstop_args
Definition: bp-table.cc:320
@ dbstop_in
Definition: bp-table.cc:321
@ dbstop_if
Definition: bp-table.cc:323
@ dbstop_at
Definition: bp-table.cc:322
@ dbstop_none
Definition: bp-table.cc:324
std::string find_bkpt_list(octave_value_list slist, std::string match)
Definition: bp-table.cc:958
#define octave_stdout
Definition: pager.h:314
bool Vdebug_on_interrupt
Definition: sighandlers.cc:71
F77_RET_T len
Definition: xerbla.cc:61