Author: Armin Rigo <[email protected]>
Branch: py3k-faulthandler
Changeset: r87350:d7433f65fd0c
Date: 2016-09-23 20:05 +0200
http://bitbucket.org/pypy/pypy/changeset/d7433f65fd0c/

Log:    in-progress

diff --git a/pypy/module/faulthandler/cintf.py 
b/pypy/module/faulthandler/cintf.py
--- a/pypy/module/faulthandler/cintf.py
+++ b/pypy/module/faulthandler/cintf.py
@@ -1,4 +1,4 @@
-from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rtyper.lltypesystem import lltype, rffi, rstr
 from rpython.translator import cdir
 from rpython.translator.tool.cbuild import ExternalCompilationInfo
 
@@ -9,17 +9,30 @@
     include_dirs=[str(cwd), cdir],
     separate_module_files=[cwd.join('faulthandler.c')])
 
-def llexternal(*args, **kwargs):
-    kwargs.setdefault('releasegil', False)
+def direct_llexternal(*args, **kwargs):
+    kwargs.setdefault('_nowrapper', True)
     kwargs.setdefault('compilation_info', eci)
     return rffi.llexternal(*args, **kwargs)
 
-pypy_faulthandler_setup = llexternal(
-    'pypy_faulthandler_setup', [], lltype.Void)
-pypy_faulthandler_teardown = llexternal(
+DUMP_CALLBACK = lltype.Ptr(lltype.FuncType([], lltype.Void))
+
+pypy_faulthandler_setup = direct_llexternal(
+    'pypy_faulthandler_setup', [DUMP_CALLBACK], rffi.CCHARP)
+
+pypy_faulthandler_teardown = direct_llexternal(
     'pypy_faulthandler_teardown', [], lltype.Void)
-pypy_faulthandler_enable = llexternal(
-    'pypy_faulthandler_enable', [], lltype.Void,
-    save_err=rffi.RFFI_SAVE_ERRNO)
-pypy_faulthandler_disable = llexternal(
+
+pypy_faulthandler_enable = direct_llexternal(
+    'pypy_faulthandler_enable', [rffi.INT, rffi.INT], rffi.CCHARP)
+
+pypy_faulthandler_disable = direct_llexternal(
     'pypy_faulthandler_disable', [], lltype.Void)
+
+pypy_faulthandler_is_enabled = direct_llexternal(
+    'pypy_faulthandler_is_enabled', [], rffi.INT)
+
+pypy_faulthandler_write = direct_llexternal(
+    'pypy_faulthandler_write', [lltype.Ptr(rstr.STR)])
+
+pypy_faulthandler_write_int = direct_llexternal(
+    'pypy_faulthandler_write_int', [lltype.Signed])
diff --git a/pypy/module/faulthandler/dumper.py 
b/pypy/module/faulthandler/dumper.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/faulthandler/dumper.py
@@ -0,0 +1,58 @@
+from rpython.rtyper.annlowlevel import llstr
+from rpython.rlib import rgc
+from rpython.rlib.rvmprof import enum_all_code_objs
+from rpython.rlib.rvmprof import cintf as rvmprof_cintf
+
+from pypy.interpreter.pycode import PyCode
+from pypy.module.faulthandler.cintf import pypy_faulthandler_write
+from pypy.module.faulthandler.cintf import pypy_faulthandler_write_int
+
+#
+# xxx The dumper is tied to the internals of rvmprof. xxx
+#
+
+
+MAX_FRAME_DEPTH = 100
+
+
+def _dump(s):
+    pypy_faulthandler_write(llstr(s))
+
+def _dump_int(i):
+    pypy_faulthandler_write_int(i)
+
+
+def dump_code(pycode, this_code_id, search_code_id):
+    if this_code_id != search_code_id:
+        return 0
+    _dump('"')
+    _dump(pycode.co_filename)
+    _dump('" in ')
+    _dump(pycode.co_name)
+    _dump(" (starting at line ")
+    _dump_int(pycode.co_firstlineno)
+    _dump(")\n")
+    return 1
+
+
[email protected]_collect
+def _dump_callback():
+    """We are as careful as we can reasonably be here (i.e. not 100%,
+    but hopefully close enough).  In particular, this is written as
+    RPython but shouldn't allocate anything.
+    """
+    _dump("Stack (most recent call first):\n")
+
+    s = rvmprof_cintf.get_rvmprof_stack()
+    depth = 0
+    while s:
+        if depth >= MAX_FRAME_DEPTH:
+            _dump("  ...\n")
+            break
+        if s.c_kind == rvmprof_cintf.VMPROF_CODE_TAG:
+            code_id = s.c_value
+            _dump("  File ")
+            if enum_all_code_objs(PyCode, dump_code, code_id) == 0:
+                _dump("???\n")
+        s = s.c_next
+        depth += 1
diff --git a/pypy/module/faulthandler/faulthandler.c 
b/pypy/module/faulthandler/faulthandler.c
--- a/pypy/module/faulthandler/faulthandler.c
+++ b/pypy/module/faulthandler/faulthandler.c
@@ -3,21 +3,24 @@
 #include <signal.h>
 #include <assert.h>
 #include <errno.h>
+#include <string.h>
 
 
 typedef struct sigaction _Py_sighandler_t;
 
 typedef struct {
-    int signum;
-    int enabled;
+    const int signum;
+    volatile int enabled;
     const char* name;
     _Py_sighandler_t previous;
-    int all_threads;
 } fault_handler_t;
 
 static struct {
+    int initialized;
     int enabled;
-    int fd, all_threads;
+    volatile int fd, all_threads;
+    volatile void (*dump_traceback)(void);
+    int _current_fd;
 } fatal_error;
 
 static stack_t stack;
@@ -39,11 +42,106 @@
 static const int faulthandler_nsignals =
     sizeof(faulthandler_handlers) / sizeof(fault_handler_t);
 
+static void
+fh_write(int fd, char *str)
+{
+    (void)write(fd, str, strlen(str));
+}
 
 RPY_EXTERN
-char *pypy_faulthandler_setup(void)
+void pypy_faulthandler_write(RPyString *s)
 {
+    (void)write(fatal_error._current_fd,
+                _RPyString_AsString(s), RPyString_Size(s));
+}
+
+RPY_EXTERN
+void pypy_faulthandler_write_int(long x)
+{
+    char buf[32];
+    int count = sprintf(buf, "%ld", x);
+    (void)write(fatal_error._current_fd, buf, count);
+}
+
+
+static void
+faulthandler_dump_traceback(int fd, int all_threads)
+{
+    static volatile int reentrant = 0;
+
+    if (reentrant)
+        return;
+    reentrant = 1;
+    fatal_error._current_fd = fd;
+
+    /* XXX 'all_threads' ignored */
+    if (fatal_error.dump_traceback)
+        fatal_error.dump_traceback();
+
+    reentrant = 0;
+}
+
+
+/* Handler for SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals.
+
+   Display the current Python traceback, restore the previous handler and call
+   the previous handler.
+
+   On Windows, don't explicitly call the previous handler, because the Windows
+   signal handler would not be called (for an unknown reason). The execution of
+   the program continues at faulthandler_fatal_error() exit, but the same
+   instruction will raise the same fault (signal), and so the previous handler
+   will be called.
+
+   This function is signal-safe and should only call signal-safe functions. */
+
+static void
+faulthandler_fatal_error(int signum)
+{
+    int fd = fatal_error.fd;
+    int i;
+    fault_handler_t *handler = NULL;
+    int save_errno = errno;
+
+    for (i = 0; i < faulthandler_nsignals; i++) {
+        handler = &faulthandler_handlers[i];
+        if (handler->signum == signum)
+            break;
+    }
+
+    /* restore the previous handler */
+    if (handler->enabled) {
+        (void)sigaction(signum, &handler->previous, NULL);
+        handler->enabled = 0;
+    }
+
+    fh_write(fd, "Fatal Python error: ");
+    fh_write(fd, handler->name);
+    fh_write(fd, "\n\n");
+
+    faulthandler_dump_traceback(fd, fatal_error.all_threads);
+
+    errno = save_errno;
+#ifdef MS_WINDOWS
+    if (signum == SIGSEGV) {
+        /* don't explicitly call the previous handler for SIGSEGV in this 
signal
+           handler, because the Windows signal handler would not be called */
+        return;
+    }
+#endif
+    /* call the previous signal handler: it is called immediately if we use
+       sigaction() thanks to SA_NODEFER flag, otherwise it is deferred */
+    raise(signum);
+}
+
+
+RPY_EXTERN
+char *pypy_faulthandler_setup(void dump_callback(void))
+{
+    if (fatal_error.initialized)
+        return;
     assert(!fatal_error.enabled);
+    fatal_error.dump_callback = dump_callback;
 
     /* Try to allocate an alternate stack for faulthandler() signal handler to
      * be able to allocate memory on the stack, even on a stack overflow. If it
@@ -58,26 +156,32 @@
             stack.ss_sp = NULL;
         }
     }
+
+    fatal_error.fd = -1;
+    fatal_error.initialized = 1;
     return NULL;
 }
 
 RPY_EXTERN
 void pypy_faulthandler_teardown(void)
 {
-    pypy_faulthandler_disable();
-    free(stack.ss_sp);
-    stack.ss_sp = NULL;
+    if (fatal_error.initialized) {
+        pypy_faulthandler_disable();
+        fatal_error.initialized = 0;
+        free(stack.ss_sp);
+        stack.ss_sp = NULL;
+    }
 }
 
 RPY_EXTERN
-int pypy_faulthandler_enable(int fd, int all_threads)
+char *pypy_faulthandler_enable(int fd, int all_threads)
 {
+    /* Install the handler for fatal signals, faulthandler_fatal_error(). */
+    int i;
     fatal_error.fd = fd;
     fatal_error.all_threads = all_threads;
 
     if (!fatal_error.enabled) {
-        int i;
-
         fatal_error.enabled = 1;
 
         for (i = 0; i < faulthandler_nsignals; i++) {
@@ -97,20 +201,19 @@
             }
             err = sigaction(handler->signum, &action, &handler->previous);
             if (err) {
-                return -1;
+                return strerror(errno);
             }
             handler->enabled = 1;
         }
     }
-    return 0;
+    return NULL;
 }
 
 RPY_EXTERN
 void pypy_faulthandler_disable(void)
 {
+    int i;
     if (fatal_error.enabled) {
-        int i;
-
         fatal_error.enabled = 0;
         for (i = 0; i < faulthandler_nsignals; i++) {
             fault_handler_t *handler = &faulthandler_handlers[i];
@@ -120,6 +223,7 @@
             handler->enabled = 0;
         }
     }
+    fatal_error.fd = -1;
 }
 
 RPY_EXTERN
diff --git a/pypy/module/faulthandler/faulthandler.h 
b/pypy/module/faulthandler/faulthandler.h
--- a/pypy/module/faulthandler/faulthandler.h
+++ b/pypy/module/faulthandler/faulthandler.h
@@ -1,15 +1,18 @@
 #ifndef PYPY_FAULTHANDLER_H
 #define PYPY_FAULTHANDLER_H
 
-#include "src/precommondefs.h"
+#include "common_header.h"
 
-RPY_EXTERN char *pypy_faulthandler_setup(void);
+RPY_EXTERN char *pypy_faulthandler_setup(void dump_callback(void));
 RPY_EXTERN void pypy_faulthandler_teardown(void);
 
-RPY_EXTERN int pypy_faulthandler_enable(int fd, int all_threads);
+RPY_EXTERN char *pypy_faulthandler_enable(int fd, int all_threads);
 RPY_EXTERN void pypy_faulthandler_disable(void);
 RPY_EXTERN int pypy_faulthandler_is_enabled(void);
 
+RPY_EXTERN void pypy_faulthandler_write(RPyString *);
+RPY_EXTERN void pypy_faulthandler_write_int(long);
+
 /*
 RPY_EXTERN int pypy_faulthandler_read_null(void);
 RPY_EXTERN void pypy_faulthandler_sigsegv(void);
diff --git a/pypy/module/faulthandler/handler.py 
b/pypy/module/faulthandler/handler.py
--- a/pypy/module/faulthandler/handler.py
+++ b/pypy/module/faulthandler/handler.py
@@ -1,19 +1,22 @@
 import os
 from rpython.rtyper.lltypesystem import rffi
 from rpython.rlib.rposix import is_valid_fd
+from rpython.rlib.rarithmetic import widen
+from rpython.rtyper.annlowlevel import llhelper
 
 from pypy.interpreter.error import oefmt, exception_from_saved_errno
 from pypy.interpreter.gateway import unwrap_spec
-from pypy.module.faulthandler import cintf
+from pypy.module.faulthandler import cintf, dumper
 
 
 class Handler(object):
     def __init__(self, space):
+        "NOT_RPYTHON"
         self.space = space
+        dumper.glob.space = space
         self._cleanup_()
 
     def _cleanup_(self):
-        self.is_initialized = False
         self.fatal_error_w_file = None
 
     def check_err(self, p_err):
@@ -28,13 +31,13 @@
             if space.is_none(w_file):
                 raise oefmt(space.w_RuntimeError, "sys.stderr is None")
         elif space.isinstance_w(w_file, space.w_int):
-            fd = space.int_w(w_file)
+            fd = space.c_int_w(w_file)
             if fd < 0 or not is_valid_fd(fd):
                 raise oefmt(space.w_ValueError,
                             "file is not a valid file descriptor")
             return fd, None
 
-        fd = space.int_w(space.call_method(w_file, 'fileno'))
+        fd = space.c_int_w(space.call_method(w_file, 'fileno'))
         try:
             space.call_method(w_file, 'flush')
         except OperationError as e:
@@ -45,27 +48,24 @@
 
     def enable(self, w_file, all_threads):
         fileno, w_file = self.get_fileno_and_file(w_file)
-        if not self.is_initialized:
-            self.check_err(cintf.pypy_faulthandler_setup())
-            self.is_initialized = True
-
+        #
+        dump_callback = llhelper(cintf.DUMP_CALLBACK, dumper._dump_callback)
+        self.check_err(cintf.pypy_faulthandler_setup(dump_callback))
+        #
         self.fatal_error_w_file = w_file
-        err = cintf.pypy_faulthandler_enable(fileno, all_threads)
-        if err:
-            space = self.space
-            raise exception_from_saved_errno(space, space.w_RuntimeError)
+        self.check_err(cintf.pypy_faulthandler_enable(
+            rffi.cast(rffi.INT, fileno),
+            rffi.cast(rffi.INT, all_threads)))
 
     def disable(self):
         cintf.pypy_faulthandler_disable()
         self.fatal_error_w_file = None
 
     def is_enabled(self):
-        return (self.is_initialized and
-                bool(cintf.pypy_faulthandler_is_enabled()))
+        return bool(widen(cintf.pypy_faulthandler_is_enabled()))
 
     def finish(self):
-        if self.is_initialized:
-            cintf.pypy_faulthandler_teardown()
+        cintf.pypy_faulthandler_teardown()
         self._cleanup_()
 
 
diff --git a/rpython/rlib/rvmprof/cintf.py b/rpython/rlib/rvmprof/cintf.py
--- a/rpython/rlib/rvmprof/cintf.py
+++ b/rpython/rlib/rvmprof/cintf.py
@@ -154,3 +154,9 @@
 
 def restore_rvmprof_stack(x):
     vmprof_tl_stack.setraw(x)
+
+#
+# faulthandler support
+
+def get_rvmprof_stack():
+    return vmprof_tl_stack.getraw()
diff --git a/rpython/rlib/rvmprof/rvmprof.py b/rpython/rlib/rvmprof/rvmprof.py
--- a/rpython/rlib/rvmprof/rvmprof.py
+++ b/rpython/rlib/rvmprof/rvmprof.py
@@ -130,7 +130,10 @@
                 if code is not None:
                     uid = code._vmprof_unique_id
                     if uid != 0:
-                        callback(code, uid, arg)
+                        res = callback(code, uid, arg)
+                        if res != 0:
+                            return res
+            return 0
         CodeClass._vmprof_enum_all_code_objs = enum_all_code_objs
 
     @jit.dont_look_inside
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to