[pypy-commit] pypy py3k-faulthandler: in-progress
arigo
pypy.commits at gmail.com
Fri Sep 23 12:53:12 EDT 2016
Author: Armin Rigo <arigo at tunes.org>
Branch: py3k-faulthandler
Changeset: r87348:0c67f2b605a3
Date: 2016-09-23 18:52 +0200
http://bitbucket.org/pypy/pypy/changeset/0c67f2b605a3/
Log: in-progress
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -422,6 +422,11 @@
# To be called before using the space
self.threadlocals.enter_thread(self)
+ # Set up faulthandler even if not imported explicitly
+ if self.config.objspace.usemodule.faulthandler:
+ from pypy.module.faulthandler import handler
+ handler.startup(self)
+
# Initialize already imported builtin modules
from pypy.interpreter.module import Module
w_modules = self.sys.get('modules')
@@ -445,6 +450,11 @@
for w_mod in self.builtin_modules.values():
if isinstance(w_mod, Module) and w_mod.startup_called:
w_mod.shutdown(self)
+ #
+ # Shut down faulthandler
+ if self.config.objspace.usemodule.faulthandler:
+ from pypy.module.faulthandler import handler
+ handler.finish(self)
def wait_for_thread_shutdown(self):
"""Wait until threading._shutdown() completes, provided the threading
diff --git a/pypy/module/faulthandler/__init__.py b/pypy/module/faulthandler/__init__.py
--- a/pypy/module/faulthandler/__init__.py
+++ b/pypy/module/faulthandler/__init__.py
@@ -5,18 +5,18 @@
}
interpleveldefs = {
- 'enable': 'interp_faulthandler.enable',
- 'disable': 'interp_faulthandler.disable',
- 'is_enabled': 'interp_faulthandler.is_enabled',
- 'register': 'interp_faulthandler.register',
-
- 'dump_traceback': 'interp_faulthandler.dump_traceback',
-
- '_read_null': 'interp_faulthandler.read_null',
- '_sigsegv': 'interp_faulthandler.sigsegv',
- '_sigfpe': 'interp_faulthandler.sigfpe',
- '_sigabrt': 'interp_faulthandler.sigabrt',
- #'_sigbus': 'interp_faulthandler.sigbus',
- #'_sigill': 'interp_faulthandler.sigill',
- '_fatal_error': 'interp_faulthandler.fatal_error',
+ 'enable': 'handler.enable',
+ 'disable': 'handler.disable',
+ 'is_enabled': 'handler.is_enabled',
+# 'register': 'interp_faulthandler.register',
+#
+# 'dump_traceback': 'interp_faulthandler.dump_traceback',
+#
+# '_read_null': 'interp_faulthandler.read_null',
+# '_sigsegv': 'interp_faulthandler.sigsegv',
+# '_sigfpe': 'interp_faulthandler.sigfpe',
+# '_sigabrt': 'interp_faulthandler.sigabrt',
+# #'_sigbus': 'interp_faulthandler.sigbus',
+# #'_sigill': 'interp_faulthandler.sigill',
+# '_fatal_error': 'interp_faulthandler.fatal_error',
}
diff --git a/pypy/module/faulthandler/cintf.py b/pypy/module/faulthandler/cintf.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/faulthandler/cintf.py
@@ -0,0 +1,25 @@
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.translator import cdir
+from rpython.translator.tool.cbuild import ExternalCompilationInfo
+
+
+cwd = py.path.local(__file__).dirpath()
+eci = ExternalCompilationInfo(
+ includes=[cwd.join('faulthandler.h')],
+ include_dirs=[str(cwd), cdir],
+ separate_module_files=[cwd.join('faulthandler.c')])
+
+def llexternal(*args, **kwargs):
+ kwargs.setdefault('releasegil', False)
+ kwargs.setdefault('compilation_info', eci)
+ return rffi.llexternal(*args, **kwargs)
+
+pypy_faulthandler_setup = llexternal(
+ 'pypy_faulthandler_setup', [], lltype.Void)
+pypy_faulthandler_teardown = 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_disable', [], lltype.Void)
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
@@ -1,6 +1,135 @@
+#include "faulthandler.h"
#include <stdlib.h>
-#include "faulthandler.h"
+#include <signal.h>
+#include <assert.h>
+#include <errno.h>
+
+typedef struct sigaction _Py_sighandler_t;
+
+typedef struct {
+ int signum;
+ int enabled;
+ const char* name;
+ _Py_sighandler_t previous;
+ int all_threads;
+} fault_handler_t;
+
+static struct {
+ int enabled;
+ int fd, all_threads;
+} fatal_error;
+
+static stack_t stack;
+
+
+static fault_handler_t faulthandler_handlers[] = {
+#ifdef SIGBUS
+ {SIGBUS, 0, "Bus error", },
+#endif
+#ifdef SIGILL
+ {SIGILL, 0, "Illegal instruction", },
+#endif
+ {SIGFPE, 0, "Floating point exception", },
+ {SIGABRT, 0, "Aborted", },
+ /* define SIGSEGV at the end to make it the default choice if searching the
+ handler fails in faulthandler_fatal_error() */
+ {SIGSEGV, 0, "Segmentation fault", }
+};
+static const int faulthandler_nsignals =
+ sizeof(faulthandler_handlers) / sizeof(fault_handler_t);
+
+
+RPY_EXTERN
+char *pypy_faulthandler_setup(void)
+{
+ assert(!fatal_error.enabled);
+
+ /* 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
+ * fails, ignore the error. */
+ stack.ss_flags = 0;
+ stack.ss_size = SIGSTKSZ;
+ stack.ss_sp = malloc(stack.ss_size);
+ if (stack.ss_sp != NULL) {
+ int err = sigaltstack(&stack, NULL);
+ if (err) {
+ free(stack.ss_sp);
+ stack.ss_sp = NULL;
+ }
+ }
+ return NULL;
+}
+
+RPY_EXTERN
+void pypy_faulthandler_teardown(void)
+{
+ pypy_faulthandler_disable();
+ free(stack.ss_sp);
+ stack.ss_sp = NULL;
+}
+
+RPY_EXTERN
+int pypy_faulthandler_enable(int fd, int all_threads)
+{
+ 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++) {
+ int err;
+ struct sigaction action;
+ fault_handler_t *handler = &faulthandler_handlers[i];
+
+ action.sa_handler = faulthandler_fatal_error;
+ sigemptyset(&action.sa_mask);
+ /* Do not prevent the signal from being received from within
+ its own signal handler */
+ action.sa_flags = SA_NODEFER;
+ if (stack.ss_sp != NULL) {
+ /* Call the signal handler on an alternate signal stack
+ provided by sigaltstack() */
+ action.sa_flags |= SA_ONSTACK;
+ }
+ err = sigaction(handler->signum, &action, &handler->previous);
+ if (err) {
+ return -1;
+ }
+ handler->enabled = 1;
+ }
+ }
+ return 0;
+}
+
+RPY_EXTERN
+void pypy_faulthandler_disable(void)
+{
+ if (fatal_error.enabled) {
+ int i;
+
+ fatal_error.enabled = 0;
+ for (i = 0; i < faulthandler_nsignals; i++) {
+ fault_handler_t *handler = &faulthandler_handlers[i];
+ if (!handler->enabled)
+ continue;
+ (void)sigaction(handler->signum, &handler->previous, NULL);
+ handler->enabled = 0;
+ }
+ }
+}
+
+RPY_EXTERN
+int pypy_faulthandler_is_enabled(void)
+{
+ return fatal_error.enabled;
+}
+
+
+#if 0
int
pypy_faulthandler_read_null(void)
{
@@ -75,3 +204,4 @@
raise(SIGILL);
}
#endif
+#endif
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,9 +1,16 @@
#ifndef PYPY_FAULTHANDLER_H
#define PYPY_FAULTHANDLER_H
-#include <signal.h>
#include "src/precommondefs.h"
+RPY_EXTERN char *pypy_faulthandler_setup(void);
+RPY_EXTERN void pypy_faulthandler_teardown(void);
+
+RPY_EXTERN int 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 int pypy_faulthandler_read_null(void);
RPY_EXTERN void pypy_faulthandler_sigsegv(void);
RPY_EXTERN int pypy_faulthandler_sigfpe(void);
@@ -15,5 +22,6 @@
#ifdef SIGILL
RPY_EXTERN void pypy_faulthandler_sigill(void);
#endif
+*/
#endif /* PYPY_FAULTHANDLER_H */
diff --git a/pypy/module/faulthandler/handler.py b/pypy/module/faulthandler/handler.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/faulthandler/handler.py
@@ -0,0 +1,108 @@
+import os
+from rpython.rtyper.lltypesystem import rffi
+from rpython.rlib.rposix import is_valid_fd
+
+from pypy.interpreter.error import oefmt, exception_from_saved_errno
+from pypy.interpreter.gateway import unwrap_spec
+from pypy.module.faulthandler import cintf
+
+
+class Handler(object):
+ def __init__(self, space):
+ self.space = space
+ self._cleanup_()
+
+ def _cleanup_(self):
+ self.is_initialized = False
+ self.fatal_error_w_file = None
+
+ def check_err(self, p_err):
+ if p_err:
+ raise oefmt(self.space.w_RuntimeError, 'faulthandler: %8',
+ rffi.charp2str(p_err))
+
+ def get_fileno_and_file(self, w_file):
+ space = self.space
+ if space.is_none(w_file):
+ w_file = space.sys.get('stderr')
+ 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)
+ 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'))
+ try:
+ space.call_method(w_file, 'flush')
+ except OperationError as e:
+ if e.async(space):
+ raise
+ pass # ignore flush() error
+ return fd, w_file
+
+ 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
+
+ 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)
+
+ 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()))
+
+ def finish(self):
+ if self.is_initialized:
+ cintf.pypy_faulthandler_teardown()
+ self._cleanup_()
+
+
+def startup(space):
+ """Initialize the faulthandler logic when the space is starting
+ (this is called from baseobjspace.py)"""
+ #
+ # Call faulthandler.enable() if the PYTHONFAULTHANDLER environment variable
+ # is defined, or if sys._xoptions has a 'faulthandler' key.
+ if not os.environ.get('PYTHONFAULTHANDLER'):
+ w_options = space.sys.get('_xoptions')
+ if not space.contains(w_options, space.wrap('faulthandler')):
+ return
+ #
+ # Like CPython. Why not just call enable(space)? Maybe the goal is
+ # to let the user override the 'faulthandler' module. Maybe someone
+ # mis-uses ``"faulthandler" in sys.modules'' as a way to check if it
+ # was started by checking if it was imported at all.
+ space.appexec([], """
+ import faulthandler
+ faulthandler.enable()
+ """)
+
+def finish(space):
+ """Finalize the faulthandler logic (called from baseobjspace.py)"""
+ space.fromcache(Handler).finish()
+
+
+ at unwrap_spec(all_threads=int)
+def enable(space, w_file=None, all_threads=0):
+ "enable(file=sys.stderr, all_threads=True): enable the fault handler"
+ space.fromcache(Handler).enable(w_file, all_threads)
+
+def disable(space):
+ "disable(): disable the fault handler"
+ space.fromcache(Handler).disable()
+
+def is_enabled(space):
+ "is_enabled()->bool: check if the handler is enabled"
+ return space.wrap(space.fromcache(Handler).is_enabled())
diff --git a/pypy/module/faulthandler/test/test_faulthander.py b/pypy/module/faulthandler/test/test_faulthander.py
--- a/pypy/module/faulthandler/test/test_faulthander.py
+++ b/pypy/module/faulthandler/test/test_faulthander.py
@@ -6,7 +6,7 @@
class AppTestFaultHandler:
spaceconfig = {
- "usemodules": ["faulthandler"]
+ "usemodules": ["faulthandler", "_vmprof"]
}
def test_enable(self):
diff --git a/pypy/module/sys/__init__.py b/pypy/module/sys/__init__.py
--- a/pypy/module/sys/__init__.py
+++ b/pypy/module/sys/__init__.py
@@ -136,6 +136,7 @@
space.setitem(self.w_dict, space.wrap('thread_info'), thread_info)
def setup_after_space_initialization(self):
+ "NOT_RPYTHON"
space = self.space
if not space.config.translating:
diff --git a/rpython/rlib/rvmprof/__init__.py b/rpython/rlib/rvmprof/__init__.py
--- a/rpython/rlib/rvmprof/__init__.py
+++ b/rpython/rlib/rvmprof/__init__.py
@@ -37,3 +37,8 @@
def disable():
_get_vmprof().disable()
+
+ at specialize.arg(0)
+def enum_all_code_objs(CodeClass, callback, arg):
+ assert _was_registered(CodeClass)
+ CodeClass._vmprof_enum_all_code_objs(callback, arg)
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
@@ -1,6 +1,6 @@
import sys, os
from rpython.rlib.objectmodel import specialize, we_are_translated
-from rpython.rlib import jit, rposix
+from rpython.rlib import jit, rposix, rgc
from rpython.rlib.rvmprof import cintf
from rpython.rtyper.annlowlevel import cast_instance_to_gcref
from rpython.rtyper.annlowlevel import cast_base_ptr_to_instance
@@ -116,6 +116,22 @@
# the types of code objects
prev = self._gather_all_code_objs
self._gather_all_code_objs = gather_all_code_objs
+ #
+ # For special usages: the faulthandler pypy module uses this.
+ # It must not allocate anything.
+ @staticmethod
+ @rgc.no_collect
+ def enum_all_code_objs(callback, arg):
+ all_code_wrefs = CodeClass._vmprof_weak_list.get_all_handles()
+ i = len(all_code_wrefs) - 1
+ while i >= 0:
+ code = all_code_wrefs[i]()
+ i -= 1
+ if code is not None:
+ uid = code._vmprof_unique_id
+ if uid != 0:
+ callback(code, uid, arg)
+ CodeClass._vmprof_enum_all_code_objs = enum_all_code_objs
@jit.dont_look_inside
def enable(self, fileno, interval):
More information about the pypy-commit
mailing list