GNU Octave  8.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-2023 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 
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 
82  fcn = sys::file_ops::concat (m_class_name, fcn);
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 
95  if (lp.contains_file_in_dir (m_file, m_dir))
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
294  = parser.statement_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 
339 void bp_table::parse_dbfunction_params (const char *who,
340  const octave_value_list& args,
341  std::string& fcn_name,
342  std::string& class_name,
343  bp_table::bp_lines& lines,
344  std::string& cond)
345 {
346  int nargin = args.length ();
347  fcn_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;
359  dbstop_args tok = dbstop_none;
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  fcn_name = args(pos).string_value ();
399  if (seen_in)
400  error ("%s: Too many function names specified -- %s",
401  who, fcn_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 fcn_name.
430  class_name = fcn_name;
431  fcn_name = arg;
432  pos++;
433  break;
434  }
435 
436  }
437  else
438  {
439  // It was a line number. Get function name from debugger.
440  if (m_evaluator.in_debug_repl ())
441  fcn_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 105; # 105 is a comment; code line is at 106
542 %! dbstop in ls 123; # 123 is a comment; code line is at 126
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], [56, 106, 208, 63, 126]);
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,
674  const std::string& condition)
675 {
676  bp_lines line_info;
677  line_info.insert (line);
678 
679  bp_lines result
680  = add_breakpoints_in_function (fname, class_name, line_info, condition);
681 
682  return result.empty () ? 0 : *(result.begin ());
683 }
684 
685 // Given file name fname, find the subfunction at line and create
686 // a breakpoint there. Put the system into debug_mode.
688 bp_table::add_breakpoints_in_function (const std::string& fname,
689  const std::string& class_name,
690  const bp_table::bp_lines& lines,
691  const std::string& condition)
692 {
693  octave_user_code *main_fcn = m_evaluator.get_user_code (fname, class_name);
694 
695  if (! main_fcn)
696  error ("add_breakpoints_in_function: unable to find function '%s'\n",
697  fname.c_str ());
698 
699  condition_valid (condition); // Throw error if condition not valid.
700 
701  bp_lines retval;
702 
703  for (const auto& lineno : lines)
704  {
705  octave_user_code *dbg_fcn = find_fcn_by_line (main_fcn, lineno);
706 
707  // We've found the right (sub)function. Now insert the breakpoint.
708  bp_lines line_info;
709  line_info.insert (lineno);
710 
711  bp_lines ret_one;
712  if (dbg_fcn && add_breakpoint_1 (dbg_fcn, fname, line_info,
713  condition, ret_one))
714  {
715  if (! ret_one.empty ())
716  {
717  int line = *(ret_one.begin ());
718 
719  if (line)
720  retval.insert (line);
721  }
722  }
723  }
724 
726 
727  return retval;
728 }
729 
730 int bp_table::add_breakpoint_in_file (const std::string& file,
731  int line,
732  const std::string& condition)
733 {
734  // Duplicates what the GUI was doing previously, but this action
735  // should not be specific to the GUI.
736 
737  bp_file_info info (m_evaluator, file);
738 
739  if (! info.ok ())
740  return 0;
741 
742  return add_breakpoint_in_function (info.fcn (), info.class_name (),
743  line, condition);
744 }
745 
747 bp_table::add_breakpoints_in_file (const std::string& file,
748  const bp_lines& lines,
749  const std::string& condition)
750 {
751  // Duplicates what the GUI was doing previously, but this action
752  // should not be specific to the GUI.
753 
754  bp_file_info info (m_evaluator, file);
755 
756  if (! info.ok ())
757  return bp_lines ();
758 
759  return add_breakpoints_in_function (info.fcn (), info.class_name (),
760  lines, condition);
761 }
762 
764  const std::string& fname,
765  const bp_table::bp_lines& lines)
766 {
767  int retval = 0;
768 
769  std::string file = fcn->fcn_file_name ();
770 
771  tree_statement_list *cmds = fcn->body ();
772 
773  // FIXME: move the operation on cmds to the tree_statement_list class?
774 
775  if (cmds)
776  {
777  octave_value_list results = cmds->list_breakpoints ();
778 
779  if (results.length () > 0)
780  {
782 
783  event_manager& evmgr = interp.get_event_manager ();
784 
785  for (const auto& lineno : lines)
786  {
787  cmds->delete_breakpoint (lineno);
788 
789  if (! file.empty ())
790  evmgr.update_breakpoint (false, file, lineno);
791  }
792 
793  results = cmds->list_breakpoints ();
794 
795  auto it = m_bp_set.find (fname);
796  if (results.empty () && it != m_bp_set.end ())
797  m_bp_set.erase (it);
798  }
799 
800  retval = results.length ();
801  }
802 
803  return retval;
804 }
805 
806 int
807 bp_table::remove_breakpoint_from_function (const std::string& fname, int line)
808 {
809  bp_lines line_info;
810  line_info.insert (line);
811 
812  return remove_breakpoints_from_function (fname, line_info);
813 }
814 
815 int
817  const bp_table::bp_lines& lines)
818 {
819  int retval = 0;
820 
821  if (lines.empty ())
822  {
824  retval = results.size ();
825  }
826  else
827  {
828  octave_user_code *dbg_fcn = m_evaluator.get_user_code (fname);
829 
830  if (! dbg_fcn)
831  error ("remove_breakpoints_from_function: unable to find function %s\n",
832  fname.c_str ());
833 
834  retval = remove_breakpoint_1 (dbg_fcn, fname, lines);
835 
836  // Search subfunctions in the order they appear in the file.
837 
838  const std::list<std::string> subfcn_names
839  = dbg_fcn->subfunction_names ();
840 
841  std::map<std::string, octave_value> subfcns
842  = dbg_fcn->subfunctions ();
843 
844  for (const auto& subf_nm : subfcn_names)
845  {
846  const auto q = subfcns.find (subf_nm);
847 
848  if (q != subfcns.end ())
849  {
850  octave_user_code *dbg_subfcn = q->second.user_code_value ();
851 
852  retval += remove_breakpoint_1 (dbg_subfcn, fname, lines);
853  }
854  }
855  }
856 
858 
859  return retval;
860 }
861 
862 // Remove all breakpoints from a file, including those in subfunctions.
863 
866  bool silent)
867 {
868  bp_lines retval;
869 
870  octave_user_code *dbg_fcn = m_evaluator.get_user_code (fname);
871 
872  if (dbg_fcn)
873  {
874  std::string file = dbg_fcn->fcn_file_name ();
875 
876  tree_statement_list *cmds = dbg_fcn->body ();
877 
878  if (cmds)
879  {
881 
882  event_manager& evmgr = interp.get_event_manager ();
883 
884  retval = cmds->remove_all_breakpoints (evmgr, file);
885 
886  auto it = m_bp_set.find (fname);
887  if (it != m_bp_set.end ())
888  m_bp_set.erase (it);
889  }
890  }
891  else if (! silent)
892  error ("remove_all_breakpoints_from_function: "
893  "unable to find function %s\n", fname.c_str ());
894 
896 
897  return retval;
898 }
899 
900 int
901 bp_table::remove_breakpoint_from_file (const std::string& file, int line)
902 {
903  // Duplicates what the GUI was doing previously, but this action
904  // should not be specific to the GUI.
905 
906  bp_file_info info (m_evaluator, file);
907 
908  if (! info.ok ())
909  return 0;
910 
911  return remove_breakpoint_from_function (info.fcn (), line);
912 }
913 
914 int
915 bp_table::remove_breakpoints_from_file (const std::string& file,
916  const bp_lines& lines)
917 {
918  // Duplicates what the GUI was doing previously, but this action
919  // should not be specific to the GUI.
920 
921  bp_file_info info (m_evaluator, file);
922 
923  if (! info.ok ())
924  return 0;
925 
926  return remove_breakpoints_from_function (info.fcn (), lines);
927 }
928 
931  bool silent)
932 {
933  // Duplicates what the GUI was doing previously, but this action
934  // should not be specific to the GUI.
935 
936  bp_file_info info (m_evaluator, file);
937 
938  if (! info.ok ())
939  return bp_lines ();
940 
941  return remove_all_breakpoints_from_function (info.fcn (), silent);
942 }
943 
945 {
946  // Odd loop structure required because delete will invalidate
947  // m_bp_set iterators.
948  for (auto it = m_bp_set.cbegin (), it_next = it;
949  it != m_bp_set.cend ();
950  it = it_next)
951  {
952  ++it_next;
954  }
955 
957 }
958 
959 std::string find_bkpt_list (octave_value_list slist, std::string match)
960 {
961  std::string retval;
962 
963  for (int i = 0; i < slist.length (); i++)
964  {
965  if (slist(i).string_value () == match)
966  {
967  retval = slist(i).string_value ();
968  break;
969  }
970  }
971 
972  return retval;
973 }
974 
977 {
978  fname_bp_map retval;
979 
980  // make copy since changes may invalidate iters of m_bp_set.
981  std::set<std::string> tmp_bp_set = m_bp_set;
982 
983  for (auto& bp_fname : tmp_bp_set)
984  {
985  if (fname_list.empty ()
986  || find_bkpt_list (fname_list, bp_fname) != "")
987  {
988  octave_user_code *dbg_fcn = m_evaluator.get_user_code (bp_fname);
989 
990  if (dbg_fcn)
991  {
992  tree_statement_list *cmds = dbg_fcn->body ();
993 
994  // FIXME: move the operation on cmds to the
995  // tree_statement_list class?
996  if (cmds)
997  {
998  std::list<bp_type> bkpts = cmds->breakpoints_and_conds ();
999 
1000  if (! bkpts.empty ())
1001  retval[bp_fname] = bkpts;
1002  }
1003 
1004  // look for breakpoints in subfunctions
1005  const std::list<std::string> subf_nm
1006  = dbg_fcn->subfunction_names ();
1007 
1008  std::map<std::string, octave_value> subfcns
1009  = dbg_fcn->subfunctions ();
1010 
1011  for (const auto& subfcn_nm : subf_nm)
1012  {
1013  const auto q = subfcns.find (subfcn_nm);
1014 
1015  if (q != subfcns.end ())
1016  {
1017  octave_user_code *dbg_subfcn
1018  = q->second.user_code_value ();
1019 
1020  cmds = dbg_subfcn->body ();
1021  if (cmds)
1022  {
1023  std::list<bp_type> bkpts
1024  = cmds->breakpoints_and_conds ();
1025 
1026  if (! bkpts.empty ())
1027  {
1028  std::string key
1029  = bp_fname + '>' + dbg_subfcn->name ();
1030 
1031  retval[key] = bkpts;
1032  }
1033  }
1034  }
1035  }
1036  }
1037  }
1038  }
1039 
1040  return retval;
1041 }
1042 
1043 // Report the status of "dbstop if error ..." and "dbstop if warning ..."
1044 // If to_screen is true, the output goes to octave_stdout; otherwise it is
1045 // returned.
1046 // If dbstop if error is true but no explicit IDs are specified, the return
1047 // value will have an empty field called "errs". If IDs are specified, the
1048 // "errs" field will have a row per ID. If dbstop if error is false, there
1049 // is no "errs" field. The "warn" field is set similarly by dbstop if warning
1050 
1052 {
1053  octave_map retval;
1054 
1056  error_system& es = interp.get_error_system ();
1057 
1058  // print dbstop if error information
1059  if (es.debug_on_error ())
1060  {
1061  if (m_errors_that_stop.empty ())
1062  {
1063  if (to_screen)
1064  octave_stdout << "stop if error\n";
1065  else
1066  retval.assign ("errs", octave_value (""));
1067  }
1068  else
1069  {
1070  Cell errs (dim_vector (bp_table::m_errors_that_stop.size (), 1));
1071  int i = 0;
1072 
1073  for (const auto& e : m_errors_that_stop)
1074  {
1075  if (to_screen)
1076  octave_stdout << "stop if error " << e << "\n";
1077  else
1078  errs(i++) = e;
1079  }
1080  if (! to_screen)
1081  retval.assign ("errs", octave_value (errs));
1082  }
1083  }
1084 
1085  // print dbstop if caught error information
1086  if (es.debug_on_caught ())
1087  {
1088  if (m_caught_that_stop.empty ())
1089  {
1090  if (to_screen)
1091  octave_stdout << "stop if caught error\n";
1092  else
1093  retval.assign ("caught", octave_value (""));
1094  }
1095  else
1096  {
1097  Cell errs (dim_vector (m_caught_that_stop.size (), 1));
1098  int i = 0;
1099 
1100  for (const auto& e : m_caught_that_stop)
1101  {
1102  if (to_screen)
1103  octave_stdout << "stop if caught error " << e << "\n";
1104  else
1105  errs(i++) = e;
1106  }
1107  if (! to_screen)
1108  retval.assign ("caught", octave_value (errs));
1109  }
1110  }
1111 
1112  // print dbstop if warning information
1113  if (es.debug_on_warning ())
1114  {
1115  if (m_warnings_that_stop.empty ())
1116  {
1117  if (to_screen)
1118  octave_stdout << "stop if warning\n";
1119  else
1120  retval.assign ("warn", octave_value (""));
1121  }
1122  else
1123  {
1124  Cell warn (dim_vector (m_warnings_that_stop.size (), 1));
1125  int i = 0;
1126 
1127  for (const auto& w : m_warnings_that_stop)
1128  {
1129  if (to_screen)
1130  octave_stdout << "stop if warning " << w << "\n";
1131  else
1132  warn(i++) = w;
1133  }
1134  if (! to_screen)
1135  retval.assign ("warn", octave_value (warn));
1136  }
1137  }
1138 
1139  // print dbstop if interrupt information
1140  if (Vdebug_on_interrupt)
1141  {
1142  if (to_screen)
1143  octave_stdout << "stop if interrupt\n";
1144  else
1145  retval.assign ("intr", octave_value (""));
1146  }
1147 
1148  return retval;
1149 }
1150 
OCTAVE_END_NAMESPACE(octave)
ComplexNDArray concat(NDArray &ra, ComplexNDArray &rb, const Array< octave_idx_type > &ra_idx)
Definition: CNDArray.cc:418
static octave_user_code * find_fcn_by_line(octave_user_code *main_fcn, int lineno, int *end_line=nullptr)
Definition: bp-table.cc:609
std::string find_bkpt_list(octave_value_list slist, std::string match)
Definition: bp-table.cc:959
dbstop_args
Definition: bp-table.cc:320
@ dbstop_at
Definition: bp-table.cc:322
@ dbstop_in
Definition: bp-table.cc:321
@ dbstop_if
Definition: bp-table.cc:323
@ dbstop_none
Definition: bp-table.cc:324
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
OCTARRAY_OVERRIDABLE_FUNC_API bool isempty(void) const
Size of the specified dimension.
Definition: Array.h:651
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:414
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-base.cc:1610
OCTARRAY_OVERRIDABLE_FUNC_API T & elem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:562
Definition: Cell.h:43
Cell index(const octave_value_list &idx, bool resize_ok=false) const
Definition: Cell.cc:171
OCTINTERP_API void statement_list(std::shared_ptr< tree_statement_list > &lst)
Definition: oct-parse.cc:6849
OCTINTERP_API void reset(void)
Definition: oct-parse.cc:6747
std::string file(void) const
Definition: bp-table.cc:99
bp_file_info(tree_evaluator &tw, const std::string &file)
Definition: bp-table.cc:63
std::string m_fcn
Definition: bp-table.cc:111
std::string dir(void) const
Definition: bp-table.cc:100
std::string m_class_name
Definition: bp-table.cc:112
std::string m_file
Definition: bp-table.cc:109
bool ok(void) const
Definition: bp-table.cc:104
std::string m_dir
Definition: bp-table.cc:110
std::string class_name(void) const
Definition: bp-table.cc:102
std::string fcn(void) const
Definition: bp-table.cc:101
bool condition_valid(const std::string &cond)
Definition: bp-table.cc:279
std::set< std::string > m_caught_that_stop
Definition: bp-table.h:232
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
int remove_breakpoint_from_file(const std::string &file="", int line=1)
Definition: bp-table.cc:901
tree_evaluator & m_evaluator
Definition: bp-table.h:223
fname_bp_map get_breakpoint_list(const octave_value_list &fname_list)
Definition: bp-table.cc:976
std::set< std::string > m_warnings_that_stop
Definition: bp-table.h:233
int remove_breakpoints_from_file(const std::string &file="", const bp_lines &lines=bp_lines())
Definition: bp-table.cc:915
std::set< std::string > m_errors_that_stop
Definition: bp-table.h:231
std::set< int > bp_lines
Definition: bp-table.h:68
bp_lines remove_all_breakpoints_from_function(const std::string &fname, bool silent=false)
Definition: bp-table.cc:865
int remove_breakpoints_from_function(const std::string &fname="", const bp_lines &lines=bp_lines())
Definition: bp-table.cc:816
void dbstop_process_map_args(const octave_map &mv)
Definition: bp-table.cc:137
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
void set_stop_flag(const char *who, const std::string &condition, bool on_off)
Definition: bp-table.cc:552
void dbclear_all_signals(void)
Definition: bp-table.cc:117
int add_breakpoint_in_file(const std::string &file="", int line=1, const std::string &condition="")
Definition: bp-table.cc:730
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:688
std::set< std::string > m_bp_set
Definition: bp-table.h:226
int remove_breakpoint_from_function(const std::string &fname="", int line=1)
Definition: bp-table.cc:807
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
std::map< std::string, std::list< bp_type > > fname_bp_map
Definition: bp-table.h:78
bp_lines add_breakpoints_in_file(const std::string &file="", const bp_lines &lines=bp_lines(), const std::string &condition="")
Definition: bp-table.cc:747
bp_lines remove_all_breakpoints_from_file(const std::string &file, bool silent=false)
Definition: bp-table.cc:930
int remove_breakpoint_1(octave_user_code *fcn, const std::string &, const bp_lines &lines)
Definition: bp-table.cc:763
octave_map stop_on_err_warn_status(bool to_screen)
Definition: bp-table.cc:1051
void remove_all_breakpoints(void)
Definition: bp-table.cc:944
void parse_dbfunction_params(const char *who, const octave_value_list &args, std::string &fcn_name, std::string &class_name, bp_table::bp_lines &lines, std::string &cond)
Definition: bp-table.cc:339
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="")
event_manager & get_event_manager(void)
Definition: interpreter.h:328
error_system & get_error_system(void)
Definition: interpreter.h:251
load_path & get_load_path(void)
Definition: interpreter.h:283
bool contains_file_in_dir(const std::string &file_name, const std::string &dir_name)
Definition: load-path.cc:499
virtual bool is_user_function(void) const
Definition: ov-base.h:515
virtual octave_user_code * user_code_value(bool silent=false)
Definition: ov-base.cc:967
virtual std::string profiler_name(void) const
Definition: ov-fcn.h:84
virtual std::list< std::string > subfunction_names(void) const
Definition: ov-fcn.h:201
std::string name(void) const
Definition: ov-fcn.h:208
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
const Cell & contents(const_iterator p) const
Definition: oct-map.h:331
std::string fcn_file_name(void) const
Definition: ov-usr-fcn.h:107
octave::tree_statement_list * body(void)
Definition: ov-usr-fcn.h:120
virtual std::map< std::string, octave_value > subfunctions(void) const
Definition: ov-usr-fcn.cc:131
bool empty(void) const
Definition: ovl.h:115
octave_idx_type length(void) const
Definition: ovl.h:113
Array< octave_value > array_value(void) const
Definition: ovl.h:90
Definition: parse.h:829
OCTINTERP_API int run(void)
Definition: oct-parse.cc:9703
void reset_debug_state(void)
Definition: pt-eval.cc:1363
octave_user_code * get_user_code(const std::string &fname="", const std::string &class_name="")
Definition: pt-eval.cc:2865
bool in_debug_repl(void) const
Definition: pt-eval.cc:5003
interpreter & get_interpreter(void)
Definition: pt-eval.h:427
virtual bool is_assignment_expression(void) const
Definition: pt-exp.h:74
octave_value_list list_breakpoints(void)
Definition: pt-stmt.cc:221
std::list< bp_type > breakpoints_and_conds(void)
Definition: pt-stmt.cc:231
void delete_breakpoint(int line)
Definition: pt-stmt.cc:199
bp_table::bp_lines remove_all_breakpoints(event_manager &evmgr, const std::string &file)
Definition: pt-stmt.cc:279
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
bool is_expression(void) const
Definition: pt-stmt.h:80
tree_expression * expression(void)
Definition: pt-stmt.h:101
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
OCTINTERP_API void print_usage(void)
Definition: defun-int.h:72
void warning(const char *fmt,...)
Definition: error.cc:1054
void error(const char *fmt,...)
Definition: error.cc:979
std::string dirname(const std::string &path)
Definition: file-ops.cc:360
std::string tail(const std::string &path)
Definition: file-ops.cc:367
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)
Octave string utility functions.
#define octave_stdout
Definition: pager.h:314
static std::string dir_sep_chars
Definition: shared-fcns.h:96
bool Vdebug_on_interrupt
Definition: sighandlers.cc:71
static string_vector make_absolute(const string_vector &sv)
Definition: utils.cc:536
F77_RET_T len
Definition: xerbla.cc:61