[pypy-svn] r70742 - in pypy/branch/c-traceback/pypy: rpython/lltypesystem translator translator/c translator/c/src translator/c/test

arigo at codespeak.net arigo at codespeak.net
Thu Jan 21 13:31:35 CET 2010


Author: arigo
Date: Thu Jan 21 13:31:34 2010
New Revision: 70742

Modified:
   pypy/branch/c-traceback/pypy/rpython/lltypesystem/lloperation.py
   pypy/branch/c-traceback/pypy/translator/c/funcgen.py
   pypy/branch/c-traceback/pypy/translator/c/src/debug_traceback.h
   pypy/branch/c-traceback/pypy/translator/c/src/main.h
   pypy/branch/c-traceback/pypy/translator/c/test/test_standalone.py
   pypy/branch/c-traceback/pypy/translator/exceptiontransform.py
Log:
Change the implementation to make it more or less
work with nested tracebacks.


Modified: pypy/branch/c-traceback/pypy/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/branch/c-traceback/pypy/rpython/lltypesystem/lloperation.py	(original)
+++ pypy/branch/c-traceback/pypy/rpython/lltypesystem/lloperation.py	Thu Jan 21 13:31:34 2010
@@ -536,10 +536,11 @@
     'debug_fatalerror':     LLOp(),
     'debug_llinterpcall':   LLOp(), # Python func call 'res=arg[0](*arg[1:])'
                                     # in backends, abort() or whatever is fine
-    'debug_start_traceback':LLOp(),
-    'debug_record_traceback':LLOp(),
-    'debug_catch_exception':LLOp(),
-    'debug_print_traceback':LLOp(),
+    'debug_start_traceback':   LLOp(),
+    'debug_record_traceback':  LLOp(),
+    'debug_catch_exception':   LLOp(),
+    'debug_reraise_traceback': LLOp(),
+    'debug_print_traceback':   LLOp(),
 
     # __________ instrumentation _________
     'instrument_count':     LLOp(),

Modified: pypy/branch/c-traceback/pypy/translator/c/funcgen.py
==============================================================================
--- pypy/branch/c-traceback/pypy/translator/c/funcgen.py	(original)
+++ pypy/branch/c-traceback/pypy/translator/c/funcgen.py	Thu Jan 21 13:31:34 2010
@@ -818,18 +818,17 @@
             self.expr(op.args[2]))
 
     def OP_DEBUG_RECORD_TRACEBACK(self, op):
-        if self.functionname is None:
-            return '/* debug_record_traceback skipped: no functionname */'
-        return 'PYPY_DEBUG_RECORD_TRACEBACK("%s");' % self.functionname
+        #if self.functionname is None, we print "?" as the argument */
+        return 'PYPY_DEBUG_RECORD_TRACEBACK("%s");' % (
+            self.functionname or "?",)
 
     def OP_DEBUG_CATCH_EXCEPTION(self, op):
         gottype = self.expr(op.args[0])
         exprs = []
         for c_limited_type in op.args[1:]:
             exprs.append('%s == %s' % (gottype, self.expr(c_limited_type)))
-        return (self.OP_DEBUG_RECORD_TRACEBACK(None) +
-                ' if (%s) { pypy_debug_catch_exception(); }' % (
-                    ' || '.join(exprs),))
+        return 'PYPY_DEBUG_CATCH_EXCEPTION("%s", %s, %s);' % (
+            self.functionname or "?", gottype, ' || '.join(exprs))
 
 
 assert not USESLOTS or '__dict__' not in dir(FunctionCodeGenerator)

Modified: pypy/branch/c-traceback/pypy/translator/c/src/debug_traceback.h
==============================================================================
--- pypy/branch/c-traceback/pypy/translator/c/src/debug_traceback.h	(original)
+++ pypy/branch/c-traceback/pypy/translator/c/src/debug_traceback.h	Thu Jan 21 13:31:34 2010
@@ -2,34 +2,65 @@
  /***  C header subsection: RPython tracebacks for debugging ***/
 
 
-#define PYPY_DEBUG_TRACEBACK_DEPTH      8
-
-#define OP_DEBUG_START_TRACEBACK()                              \
-  pypy_debug_traceback_count = PYPY_DEBUG_TRACEBACK_DEPTH
+/* We store a list of (location, exctype) in a circular buffer that
+   we hope is large enough.  Example of how to interpret the content
+   of the buffer:
+
+       location   exctype      meaning
+
+       NULL       &KeyError    a KeyError was raised
+       h:5        NULL         it was raised at h:5
+       g:12       NULL         which itself was called from g:12
+       f:17       &KeyError    called from f:17, where a finally block starts
+       ...                     ...more exceptions can occur...
+       RERAISE    &KeyError    eventually the KeyError is re-raised by f
+       entry:25   NULL         which itself was called from entry:25
+
+   Note that decoding the buffer assumes that when exctype matches, it was
+   really the same exception, for the purpose of going back from the RERAISE
+   line to the f:17/KeyError line.
+*/
 
-#define OP_DEBUG_PRINT_TRACEBACK()                              \
-  pypy_debug_traceback_print()
+#define PYPY_DEBUG_TRACEBACK_DEPTH        128     /* a power of two */
 
-#define PYPY_DEBUG_RECORD_TRACEBACK(funcname)                           \
-  if ((--pypy_debug_traceback_count) >= 0) {                            \
-    static struct pydtentry_s entry = { PYPY_FILE_NAME, funcname, __LINE__ }; \
-    pypy_debug_tracebacks[pypy_debug_traceback_count] = &entry;         \
+#define PYPYDTPOS_RERAISE                 ((struct pypydtpos_s *) -1)
+#define PYPYDTSTORE(loc, etype)                         \
+  pypy_debug_tracebacks[pypydtcount].location = loc;    \
+  pypy_debug_tracebacks[pypydtcount].exctype = etype;   \
+  pypydtcount = (pypydtcount + 1) & (PYPY_DEBUG_TRACEBACK_DEPTH-1)
+
+#define OP_DEBUG_START_TRACEBACK(etype, _)  PYPYDTSTORE(NULL, etype)
+#define OP_DEBUG_RERAISE_TRACEBACK(etp, _)  PYPYDTSTORE(PYPYDTPOS_RERAISE, etp)
+#define OP_DEBUG_PRINT_TRACEBACK()          pypy_debug_traceback_print()
+
+#define PYPY_DEBUG_RECORD_TRACEBACK(funcname)   {       \
+    static struct pypydtpos_s loc = {                   \
+      PYPY_FILE_NAME, funcname, __LINE__ };             \
+    PYPYDTSTORE(&loc, NULL);                            \
+  }
+#define PYPY_DEBUG_CATCH_EXCEPTION(funcname, etype, is_fatal)   {       \
+    static struct pypydtpos_s loc = {                                   \
+      PYPY_FILE_NAME, funcname, __LINE__ };                             \
+    PYPYDTSTORE(&loc, etype);                                           \
+    if (is_fatal) pypy_debug_catch_fatal_exception();                   \
   }
 
-/* Format of the data: to represent a location in the source code, we
-   use for now just a pointer to a 'pypy_debug_traceback_entry_s'.
-*/
-struct pydtentry_s {
+struct pypydtpos_s {
   const char *filename;
   const char *funcname;
   int lineno;
 };
 
-extern int pypy_debug_traceback_count;
-extern struct pydtentry_s *pypy_debug_tracebacks[PYPY_DEBUG_TRACEBACK_DEPTH];
+struct pypydtentry_s {
+  struct pypydtpos_s *location;
+  void *exctype;
+};
+
+extern int pypydtcount;
+extern struct pypydtentry_s pypy_debug_tracebacks[PYPY_DEBUG_TRACEBACK_DEPTH];
 
 void pypy_debug_traceback_print(void);
-void pypy_debug_catch_exception(void);
+void pypy_debug_catch_fatal_exception(void);
 
 
 /************************************************************/
@@ -37,29 +68,56 @@
 
 #ifndef PYPY_NOT_MAIN_FILE
 
-int pypy_debug_traceback_count = PYPY_DEBUG_TRACEBACK_DEPTH;
-struct pydtentry_s *pypy_debug_tracebacks[PYPY_DEBUG_TRACEBACK_DEPTH];
+int pypydtcount = 0;
+struct pypydtentry_s pypy_debug_tracebacks[PYPY_DEBUG_TRACEBACK_DEPTH];
 
 void pypy_debug_traceback_print(void)
 {
-  int i, lineno;
-  const char *filename;
-  const char *funcname;
+  int i;
+  int skipping;
+  void *my_etype = RPyFetchExceptionType();
+  struct pypydtpos_s *location;
+  void *etype;
+  int has_loc;
 
+  /* This code parses the pypy_debug_tracebacks array.  See example
+     at the start of the file. */
   fprintf(stderr, "RPython traceback:\n");
-  for (i = 0; i < PYPY_DEBUG_TRACEBACK_DEPTH; i++)
+  skipping = 0;
+  i = (pypydtcount - 1) & (PYPY_DEBUG_TRACEBACK_DEPTH-1);
+  while (i != pypydtcount)
     {
-      if (i < pypy_debug_traceback_count)
-        continue;
-      filename = pypy_debug_tracebacks[i]->filename;
-      funcname = pypy_debug_tracebacks[i]->funcname;
-      lineno   = pypy_debug_tracebacks[i]->lineno;
-      fprintf(stderr, "  File \"%s\", line %d, in %s\n",
-              filename, lineno, funcname);
+      location = pypy_debug_tracebacks[i].location;
+      etype    = pypy_debug_tracebacks[i].exctype;
+      has_loc  = location != NULL && location != PYPYDTPOS_RERAISE;
+
+      if (skipping && has_loc && etype == my_etype)
+        skipping = 0;     /* found the matching "f:17, &KeyError */
+
+      if (!skipping)
+        {
+          if (has_loc)
+            fprintf(stderr, "  File \"%s\", line %d, in %s\n",
+                    location->filename, location->lineno, location->funcname);
+          else
+            {
+              /* line "NULL, &KeyError" or "RERAISE, &KeyError" */
+              if (etype != my_etype)
+                {
+                  fprintf(stderr, "  Note: this traceback is "
+                                  "incomplete or corrupted!\n");
+                  break;
+                }
+              if (location == NULL)  /* found the place that raised the exc */
+                break;
+              skipping = 1;     /* RERAISE: skip until "f:17, &KeyError" */
+            }
+        }
+      i = (i - 1) & (PYPY_DEBUG_TRACEBACK_DEPTH-1);
     }
 }
 
-void pypy_debug_catch_exception(void)
+void pypy_debug_catch_fatal_exception(void)
 {
   pypy_debug_traceback_print();
   fprintf(stderr, "Fatal RPython error: %s\n",

Modified: pypy/branch/c-traceback/pypy/translator/c/src/main.h
==============================================================================
--- pypy/branch/c-traceback/pypy/translator/c/src/main.h	(original)
+++ pypy/branch/c-traceback/pypy/translator/c/src/main.h	Thu Jan 21 13:31:34 2010
@@ -36,8 +36,8 @@
 
     exitcode = STANDALONE_ENTRY_POINT(list);
     if (RPyExceptionOccurred()) {
-        /* fish for the exception type, at least */
-        pypy_debug_catch_exception();
+        /* print the RPython traceback */
+        pypy_debug_catch_fatal_exception();
     }
     return exitcode;
 

Modified: pypy/branch/c-traceback/pypy/translator/c/test/test_standalone.py
==============================================================================
--- pypy/branch/c-traceback/pypy/translator/c/test/test_standalone.py	(original)
+++ pypy/branch/c-traceback/pypy/translator/c/test/test_standalone.py	Thu Jan 21 13:31:34 2010
@@ -413,7 +413,8 @@
         assert lines2[-2] != lines[-2]    # different line number
         assert lines2[-3] == lines[-3]    # same line number
 
-    def test_fatal_error_finally(self):
+    def test_fatal_error_finally_1(self):
+        # a simple case of try:finally:
         def g(x):
             if x == 1:
                 raise KeyError
@@ -439,6 +440,77 @@
         assert re.match(r'  File "\w+.c", line \d+, in pypy_g_h', l2)
         assert re.match(r'  File "\w+.c", line \d+, in pypy_g_g', l3)
 
+    def test_fatal_error_finally_2(self):
+        # a try:finally: in which we raise and catch another exception
+        def raiseme(x):
+            if x == 1:
+                raise ValueError
+        def raise_and_catch(x):
+            try:
+                raiseme(x)
+            except ValueError:
+                pass
+        def g(x):
+            if x == 1:
+                raise KeyError
+        def h(x):
+            try:
+                g(x)
+            finally:
+                raise_and_catch(x)
+                os.write(1, 'done.\n')
+        def entry_point(argv):
+            if len(argv) < 3:
+                h(len(argv))
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        #
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == 'done.'
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'Fatal RPython error: KeyError'
+        assert len(lines) >= 5
+        l0, l1, l2, l3 = lines[-5:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in pypy_g_entry_point', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in pypy_g_h', l2)
+        assert re.match(r'  File "\w+.c", line \d+, in pypy_g_g', l3)
+
+    def test_fatal_error_finally_3(self):
+        py.test.skip("not implemented: "
+                     "a try:finally: in which we raise the *same* exception")
+
+    def test_fatal_error_finally_4(self):
+        # a try:finally: in which we raise (and don't catch) an exception
+        def raiseme(x):
+            if x == 1:
+                raise ValueError
+        def g(x):
+            if x == 1:
+                raise KeyError
+        def h(x):
+            try:
+                g(x)
+            finally:
+                raiseme(x)
+                os.write(1, 'done.\n')
+        def entry_point(argv):
+            if len(argv) < 3:
+                h(len(argv))
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        #
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == ''
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'Fatal RPython error: ValueError'
+        assert len(lines) >= 5
+        l0, l1, l2, l3 = lines[-5:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in pypy_g_entry_point', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in pypy_g_h', l2)
+        assert re.match(r'  File "\w+.c", line \d+, in pypy_g_raiseme', l3)
+
     def test_assertion_error(self):
         def g(x):
             assert x != 1

Modified: pypy/branch/c-traceback/pypy/translator/exceptiontransform.py
==============================================================================
--- pypy/branch/c-traceback/pypy/translator/exceptiontransform.py	(original)
+++ pypy/branch/c-traceback/pypy/translator/exceptiontransform.py	Thu Jan 21 13:31:34 2010
@@ -89,11 +89,12 @@
             # assert(!RPyExceptionOccurred());
             exc_data.exc_type = etype
             exc_data.exc_value = evalue
-            lloperation.llop.debug_start_traceback(lltype.Void)
+            lloperation.llop.debug_start_traceback(lltype.Void, etype)
 
         def rpyexc_reraise(etype, evalue):
             exc_data.exc_type = etype
             exc_data.exc_value = evalue
+            lloperation.llop.debug_reraise_traceback(lltype.Void, etype)
 
         def rpyexc_fetch_exception():
             evalue = rpyexc_fetch_value()
@@ -325,22 +326,24 @@
 
     def transform_jump_to_except_block(self, graph, entrymap, link):
         reraise = self.comes_from_last_exception(entrymap, link)
-        if reraise:
-            fnptr = self.rpyexc_reraise_ptr
-        else:
-            fnptr = self.rpyexc_raise_ptr
         result = Variable()
         result.concretetype = lltype.Void
         block = Block([copyvar(None, v)
                        for v in graph.exceptblock.inputargs])
-        block.operations = [
-            SpaceOperation("direct_call",
-                           [fnptr] + block.inputargs,
-                           result)]
-        if not reraise:
-            block.operations.append(
+        if reraise:
+            block.operations = [
+                SpaceOperation("direct_call",
+                               [self.rpyexc_reraise_ptr] + block.inputargs,
+                               result),
+                ]
+        else:
+            block.operations = [
+                SpaceOperation("direct_call",
+                               [self.rpyexc_raise_ptr] + block.inputargs,
+                               result),
                 SpaceOperation('debug_record_traceback', [],
-                               varoftype(lltype.Void)))
+                               varoftype(lltype.Void)),
+                ]
         link.target = block
         RETTYPE = graph.returnblock.inputargs[0].concretetype
         l = Link([error_constant(RETTYPE)], graph.returnblock)



More information about the Pypy-commit mailing list