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