changeset 20666:e0e2c2ce7e94

defer stack trace until back at top level * error.cc (debug_or_throw_exception): New arg, show_stack_trace. Store stack trace info in exception object. Change all uses. (pr_where, pr_where_1, pr_where_2): New arg, os. Write to it instead of writing to std::cerr unconditionally. Change all uses. (error_1): Don't print stack trace here, just determine whether stack trace should be shown by the current exception. * toplev.cc (main_loop): Display stack trace when catching octave_execution_exception. * ov-base.cc (octave_base_value::cell_value, octave_base_value::string_value): Use gripe_wrong_type_arg. * oct-parse.in.yy (Feval, Fevalin): Also call recover_from_exception when handling octave_execution_exception. * pt-eval.cc (tree_evaluator::visit_try_catch_command, tree_evaluator::do_unwind_protect_cleanup): Also call recover_from_exception when handling octave_execution_exception. * quit.h (octave_execution_exception): Extend class to handle a text message for stack trace info.
author John W. Eaton <jwe@octave.org>
date Fri, 23 Oct 2015 16:55:23 -0400
parents 67e6343cd29a
children 8742e0b1cc49
files libinterp/corefcn/error.cc libinterp/corefcn/toplev.cc libinterp/octave-value/ov-base.cc libinterp/parse-tree/oct-parse.in.yy libinterp/parse-tree/pt-eval.cc liboctave/cruft/misc/quit.h
diffstat 6 files changed, 151 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/error.cc	Wed Oct 28 22:34:43 2015 +0000
+++ b/libinterp/corefcn/error.cc	Fri Oct 23 16:55:23 2015 -0400
@@ -134,63 +134,6 @@
 }
 
 static void
-debug_or_throw_exception (void)
-{
-  if ((interactive || forced_interactive)
-      && Vdebug_on_error && octave_call_stack::caller_user_code ())
-    {
-      unwind_protect frame;
-      frame.protect_var (Vdebug_on_error);
-      Vdebug_on_error = false;
-
-      tree_evaluator::debug_mode = true;
-
-      tree_evaluator::current_frame = octave_call_stack::current_frame ();
-
-      do_keyboard (octave_value_list ());
-    }
-  else
-    octave_throw_execution_exception ();
-}
-
-// Warning messages are never buffered.
-
-static void
-vwarning (const char *name, const char *id, const char *fmt, va_list args)
-{
-  if (discard_warning_messages)
-    return;
-
-  flush_octave_stdout ();
-
-  std::ostringstream output_buf;
-
-  octave_vformat (output_buf, fmt, args);
-
-  // FIXME: we really want to capture the message before it has all the
-  //        formatting goop attached to it.  We probably also want just the
-  //        message, not the traceback information.
-
-  std::string base_msg = output_buf.str ();
-  std::string msg_string;
-
-  if (name)
-    msg_string = std::string (name) + ": ";
-
-  msg_string += base_msg + "\n";
-
-  Vlast_warning_id = id;
-  Vlast_warning_message = base_msg;
-
-  if (! Vquiet_warning)
-    {
-      octave_diary << msg_string;
-
-      std::cerr << msg_string;
-    }
-}
-
-static void
 verror (bool save_last_error, std::ostream& os,
         const char *name, const char *id, const char *fmt, va_list args,
         bool with_cfn = false)
@@ -272,7 +215,7 @@
 }
 
 static void
-pr_where_2 (const char *fmt, va_list args)
+pr_where_2 (std::ostream& os, const char *fmt, va_list args)
 {
   if (fmt)
     {
@@ -288,12 +231,12 @@
                     {
                       char *tmp_fmt = strsave (fmt);
                       tmp_fmt[len - 1] = '\0';
-                      verror (false, std::cerr, 0, "", tmp_fmt, args);
+                      verror (false, os, 0, "", tmp_fmt, args);
                       delete [] tmp_fmt;
                     }
                 }
               else
-                verror (false, std::cerr, 0, "", fmt, args);
+                verror (false, os, 0, "", fmt, args);
             }
         }
     }
@@ -302,16 +245,16 @@
 }
 
 static void
-pr_where_1 (const char *fmt, ...)
+pr_where_1 (std::ostream& os, const char *fmt, ...)
 {
   va_list args;
   va_start (args, fmt);
-  pr_where_2 (fmt, args);
+  pr_where_2 (os, fmt, args);
   va_end (args);
 }
 
 static void
-pr_where (const char *who)
+pr_where (std::ostream& os, const char *who)
 {
   std::list<octave_call_stack::stack_frame> frames
     = octave_call_stack::backtrace_frames ();
@@ -319,7 +262,7 @@
   size_t nframes = frames.size ();
 
   if (nframes > 0)
-    pr_where_1 ("%s: called from\n", who);
+    pr_where_1 (os, "%s: called from\n", who);
 
   // Print the error message only if it is different from the previous one;
   // Makes the output more concise and readable.
@@ -334,11 +277,85 @@
       int line = elt.line ();
       int column = elt.column ();
 
-      pr_where_1 ("    %s at line %d column %d\n",
+      pr_where_1 (os, "    %s at line %d column %d\n",
                   fcn_name.c_str (), line, column);
     }
 }
 
+static void
+debug_or_throw_exception (bool show_stack_trace = false)
+{
+  if ((interactive || forced_interactive)
+      && Vdebug_on_error && octave_call_stack::caller_user_code ())
+    {
+      unwind_protect frame;
+      frame.protect_var (Vdebug_on_error);
+      Vdebug_on_error = false;
+
+      tree_evaluator::debug_mode = true;
+
+      tree_evaluator::current_frame = octave_call_stack::current_frame ();
+
+      if (show_stack_trace)
+        pr_where (std::cerr, "error");
+
+      do_keyboard (octave_value_list ());
+    }
+  else
+    {
+      octave_execution_exception e;
+
+      if (show_stack_trace
+          && octave_exception_state != octave_exec_exception)
+        {
+          std::ostringstream buf;
+          pr_where (buf, "error");
+          e.set_stack_trace (buf.str ());
+        }
+
+      octave_exception_state = octave_exec_exception;
+
+      throw e;
+    }
+}
+
+// Warning messages are never buffered.
+
+static void
+vwarning (const char *name, const char *id, const char *fmt, va_list args)
+{
+  if (discard_warning_messages)
+    return;
+
+  flush_octave_stdout ();
+
+  std::ostringstream output_buf;
+
+  octave_vformat (output_buf, fmt, args);
+
+  // FIXME: we really want to capture the message before it has all the
+  //        formatting goop attached to it.  We probably also want just the
+  //        message, not the traceback information.
+
+  std::string base_msg = output_buf.str ();
+  std::string msg_string;
+
+  if (name)
+    msg_string = std::string (name) + ": ";
+
+  msg_string += base_msg + "\n";
+
+  Vlast_warning_id = id;
+  Vlast_warning_message = base_msg;
+
+  if (! Vquiet_warning)
+    {
+      octave_diary << msg_string;
+
+      std::cerr << msg_string;
+    }
+}
+
 void
 vmessage (const char *name, const char *fmt, va_list args)
 {
@@ -412,6 +429,8 @@
 error_1 (std::ostream& os, const char *name, const char *id,
          const char *fmt, va_list args, bool with_cfn = false)
 {
+  bool show_stack_trace = false;
+
   if (fmt)
     {
       if (*fmt)
@@ -437,7 +456,7 @@
                   bool in_user_code = octave_call_stack::caller_user_code () != 0;
 
                   if (in_user_code && ! discard_error_messages)
-                    pr_where ("error");
+                    show_stack_trace = true;
                 }
             }
         }
@@ -445,7 +464,7 @@
   else
     panic ("error_1: invalid format");
 
-  debug_or_throw_exception ();
+  debug_or_throw_exception (show_stack_trace);
 }
 
 void
@@ -632,7 +651,7 @@
       if (! fmt_suppresses_backtrace && in_user_code
           && Vbacktrace_on_warning
           && ! discard_warning_messages)
-        pr_where ("warning");
+        pr_where (std::cerr, "warning");
 
       if ((interactive || forced_interactive)
           && Vdebug_on_warning && in_user_code)
@@ -896,10 +915,11 @@
                       if (l > 0)
                         {
                           if (c > 0)
-                            pr_where_1 ("error: near line %d, column %d",
+                            pr_where_1 (std::cerr,
+                                        "error: near line %d, column %d",
                                         l, c);
                           else
-                            pr_where_1 ("error: near line %d", l);
+                            pr_where_1 (std::cerr, "error: near line %d", l);
                         }
                     }
                   else
@@ -907,10 +927,12 @@
                       if (l > 0)
                         {
                           if (c > 0)
-                            pr_where_1 ("error: called from '%s' near line %d, column %d",
+                            pr_where_1 (std::cerr,
+                                        "error: called from '%s' near line %d, column %d",
                                         nm.c_str (), l, c);
                           else
-                            pr_where_1 ("error: called from '%d' near line %d",
+                            pr_where_1 (std::cerr,
+                                        "error: called from '%d' near line %d",
                                         nm.c_str (), l);
                         }
                     }
@@ -922,10 +944,12 @@
                       if (l > 0)
                         {
                           if (c > 0)
-                            pr_where_1 ("error: in file %s near line %d, column %d",
+                            pr_where_1 (std::cerr,
+                                        "error: in file %s near line %d, column %d",
                                         file.c_str (), l, c);
                           else
-                            pr_where_1 ("error: in file %s near line %d",
+                            pr_where_1 (std::cerr,
+                                        "error: in file %s near line %d",
                                         file.c_str (), l);
                         }
                     }
@@ -934,10 +958,12 @@
                       if (l > 0)
                         {
                           if (c > 0)
-                            pr_where_1 ("error: called from '%s' in file %s near line %d, column %d",
+                            pr_where_1 (std::cerr,
+                                        "error: called from '%s' in file %s near line %d, column %d",
                                         nm.c_str (), file.c_str (), l, c);
                           else
-                            pr_where_1 ("error: called from '%d' in file %s near line %d",
+                            pr_where_1 (std::cerr,
+                                        "error: called from '%d' in file %s near line %d",
                                         nm.c_str (), file.c_str (), l);
                         }
                     }
--- a/libinterp/corefcn/toplev.cc	Wed Oct 28 22:34:43 2015 +0000
+++ b/libinterp/corefcn/toplev.cc	Fri Oct 23 16:55:23 2015 -0400
@@ -665,8 +665,13 @@
                     << e.message () << " -- trying to return to prompt"
                     << std::endl;
         }
-      catch (const octave_execution_exception&)
+      catch (const octave_execution_exception& e)
         {
+          std::string stack_trace = e.info ();
+
+          if (! stack_trace.empty ())
+            std::cerr << stack_trace;
+
           recover_from_exception ();
         }
       catch (const std::bad_alloc&)
--- a/libinterp/octave-value/ov-base.cc	Wed Oct 28 22:34:43 2015 +0000
+++ b/libinterp/octave-value/ov-base.cc	Fri Oct 23 16:55:23 2015 -0400
@@ -549,9 +549,7 @@
 
   try
     {
-      std::string tn = type_name ();
-
-      error ("wrong type argument '%s'\n", tn.c_str ());
+      gripe_wrong_type_arg ("cell value", type_name ());
     }
   catch (const octave_execution_exception&)
     {
@@ -949,9 +947,7 @@
 
   try
     {
-      std::string tn = type_name ();
-
-      error ("wrong type argument '%s'\n", tn.c_str ());
+      gripe_wrong_type_arg ("string value", type_name ());
     }
   catch (const octave_execution_exception&)
     {
--- a/libinterp/parse-tree/oct-parse.in.yy	Wed Oct 28 22:34:43 2015 +0000
+++ b/libinterp/parse-tree/oct-parse.in.yy	Fri Oct 23 16:55:23 2015 -0400
@@ -4965,6 +4965,8 @@
         }
       catch (const octave_execution_exception&)
         {
+          recover_from_exception ();
+
           execution_error = true;
         }
 
@@ -5122,6 +5124,8 @@
         }
       catch (const octave_execution_exception&)
         {
+          recover_from_exception ();
+
           execution_error = true;
         }
 
--- a/libinterp/parse-tree/pt-eval.cc	Wed Oct 28 22:34:43 2015 +0000
+++ b/libinterp/parse-tree/pt-eval.cc	Fri Oct 23 16:55:23 2015 -0400
@@ -42,6 +42,7 @@
 #include "pt-all.h"
 #include "pt-eval.h"
 #include "symtab.h"
+#include "toplev.h"
 #include "unwind-prot.h"
 
 //FIXME: This should be part of tree_evaluator
@@ -867,6 +868,8 @@
         }
       catch (const octave_execution_exception&)
         {
+          recover_from_exception ();
+
           execution_error = true;
         }
     }
@@ -937,6 +940,8 @@
     }
   catch (const octave_execution_exception&)
     {
+      recover_from_exception ();
+
       execution_error_in_cleanup = true;
     }
 
@@ -1011,6 +1016,8 @@
         }
       catch (const octave_execution_exception&)
         {
+          recover_from_exception ();
+
           unwind_protect_exception = true;
 
           // Run the cleanup code on exceptions, so that it is run even in case
--- a/liboctave/cruft/misc/quit.h	Wed Oct 28 22:34:43 2015 +0000
+++ b/liboctave/cruft/misc/quit.h	Fri Oct 23 16:55:23 2015 -0400
@@ -30,6 +30,7 @@
 
 #ifdef __cplusplus
 #include <new>
+#include <string>
 extern "C" {
 #endif
 
@@ -75,6 +76,35 @@
 class
 octave_execution_exception
 {
+public:
+
+  octave_execution_exception (void) : m_stack_trace () { }
+
+  octave_execution_exception (const octave_execution_exception& x)
+    : m_stack_trace (x.m_stack_trace) { }
+
+  octave_execution_exception& operator = (const octave_execution_exception& x)
+  {
+    if (&x != this)
+      m_stack_trace = x.m_stack_trace;
+
+    return *this;
+  }
+
+  ~octave_execution_exception (void) { }
+
+  virtual void set_stack_trace (const std::string& st)
+  {
+    m_stack_trace = st;
+  }
+
+  virtual std::string info (void) const
+  {
+    return m_stack_trace;
+  }
+
+private:
+  std::string m_stack_trace;
 };
 
 class