[pypy-commit] pypy py3k-test-cpyext: hg merge py3k

rlamy pypy.commits at gmail.com
Mon Oct 3 15:22:26 EDT 2016


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: py3k-test-cpyext
Changeset: r87555:295f0e0f53a0
Date: 2016-10-03 20:21 +0100
http://bitbucket.org/pypy/pypy/changeset/295f0e0f53a0/

Log:	hg merge py3k

diff too long, truncating to 2000 out of 2395 lines

diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -40,13 +40,14 @@
     "binascii", "_multiprocessing", '_warnings', "_collections",
     "_multibytecodec", "_continuation", "_cffi_backend",
     "_csv", "_pypyjson", "_posixsubprocess", # "cppyy", "micronumpy"
-    "faulthandler", "_jitlog",
+    "_jitlog",
 ])
 
 from rpython.jit.backend import detect_cpu
 try:
     if detect_cpu.autodetect().startswith('x86'):
         working_modules.add('_vmprof')
+        working_modules.add('faulthandler')
 except detect_cpu.ProcessorAutodetectError:
     pass
 
@@ -93,6 +94,7 @@
                          ('objspace.usemodules.thread', True)],
     'cpyext': [('objspace.usemodules.array', True)],
     'cppyy': [('objspace.usemodules.cpyext', True)],
+    'faulthandler': [('objspace.usemodules._vmprof', True)],
     }
 module_suggests = {
     # the reason you want _rawffi is for ctypes, which
@@ -118,7 +120,8 @@
     "_hashlib"  : ["pypy.module._ssl.interp_ssl"],
     "_minimal_curses": ["pypy.module._minimal_curses.fficurses"],
     "_continuation": ["rpython.rlib.rstacklet"],
-    "_vmprof" : ["pypy.module._vmprof.interp_vmprof"],
+    "_vmprof"      : ["pypy.module._vmprof.interp_vmprof"],
+    "faulthandler" : ["pypy.module._vmprof.interp_vmprof"],
     "_lzma"     : ["pypy.module._lzma.interp_lzma"],
     }
 
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -39,7 +39,7 @@
 from somewhere else.  You no longer have to do the same with the
 ``pypy`` executable, as long as it finds its ``libpypy-c.so`` library.
 
-.. branch: _warning
+.. branch: _warnings
 
 CPython allows warning.warn(('something', 1), Warning), on PyPy this
 produced a "expected a readable buffer object" error. Test and fix.
@@ -49,6 +49,11 @@
 CPython rejects 'a'.strip(buffer(' ')); only None, str or unicode are
 allowed as arguments. Test and fix for str and unicode
 
+.. branch: faulthandler
+
+Port the 'faulthandler' module to PyPy default.  This module is standard
+in Python 3.3 but can also be installed from CPython >= 2.6 from PyPI.
+
 .. branch: test-cpyext
 
 Refactor cpyext testing to be more pypy3-friendly.
diff --git a/pypy/interpreter/app_main.py b/pypy/interpreter/app_main.py
--- a/pypy/interpreter/app_main.py
+++ b/pypy/interpreter/app_main.py
@@ -31,6 +31,7 @@
 arg ...: arguments passed to program in sys.argv[1:]
 PyPy options and arguments:
 --info : print translation information about this PyPy executable
+-X faulthandler: attempt to display tracebacks when PyPy crashes
 """
 # Missing vs CPython: PYTHONHOME
 USAGE2 = """
@@ -518,6 +519,14 @@
     sys._xoptions = dict(x.split('=', 1) if '=' in x else (x, True)
                          for x in options['_xoptions'])
 
+    if 'faulthandler' in sys.builtin_module_names:
+        if 'faulthandler' in sys._xoptions or os.getenv('PYTHONFAULTHANDLER'):
+            import faulthandler
+            try:
+                faulthandler.enable(2)   # manually set to stderr
+            except ValueError:
+                pass      # ignore "2 is not a valid file descriptor"
+
 ##    if not we_are_translated():
 ##        for key in sorted(options):
 ##            print '%40s: %s' % (key, options[key])
diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py
--- a/pypy/module/__pypy__/__init__.py
+++ b/pypy/module/__pypy__/__init__.py
@@ -2,6 +2,8 @@
 
 from pypy.interpreter.mixedmodule import MixedModule
 from pypy.module.imp.importing import get_pyc_magic
+from rpython.rlib import rtime
+
 
 class BuildersModule(MixedModule):
     appleveldefs = {}
@@ -14,16 +16,11 @@
 class TimeModule(MixedModule):
     appleveldefs = {}
     interpleveldefs = {}
-    if sys.platform.startswith("linux") or 'bsd' in sys.platform:
-        from pypy.module.__pypy__ import interp_time
+    if rtime.HAS_CLOCK_GETTIME:
         interpleveldefs["clock_gettime"] = "interp_time.clock_gettime"
         interpleveldefs["clock_getres"] = "interp_time.clock_getres"
-        for name in [
-            "CLOCK_REALTIME", "CLOCK_MONOTONIC", "CLOCK_MONOTONIC_RAW",
-            "CLOCK_PROCESS_CPUTIME_ID", "CLOCK_THREAD_CPUTIME_ID"
-        ]:
-            if getattr(interp_time, name) is not None:
-                interpleveldefs[name] = "space.wrap(interp_time.%s)" % name
+        for name in rtime.ALL_DEFINED_CLOCKS:
+            interpleveldefs[name] = "space.wrap(%d)" % getattr(rtime, name)
 
 
 class ThreadModule(MixedModule):
diff --git a/pypy/module/__pypy__/interp_time.py b/pypy/module/__pypy__/interp_time.py
--- a/pypy/module/__pypy__/interp_time.py
+++ b/pypy/module/__pypy__/interp_time.py
@@ -4,71 +4,28 @@
 from pypy.interpreter.error import exception_from_saved_errno
 from pypy.interpreter.gateway import unwrap_spec
 from rpython.rtyper.lltypesystem import rffi, lltype
-from rpython.rtyper.tool import rffi_platform
-from rpython.translator.tool.cbuild import ExternalCompilationInfo
+from rpython.rlib import rtime
+from rpython.rlib.rtime import HAS_CLOCK_GETTIME
 
-if sys.platform == 'linux2':
-    libraries = ["rt"]
-else:
-    libraries = []
-
-
-class CConfig:
-    _compilation_info_ = ExternalCompilationInfo(
-        includes=["time.h"],
-        libraries=libraries,
-    )
-
-    HAS_CLOCK_GETTIME = rffi_platform.Has('clock_gettime')
-
-    CLOCK_REALTIME = rffi_platform.DefinedConstantInteger("CLOCK_REALTIME")
-    CLOCK_MONOTONIC = rffi_platform.DefinedConstantInteger("CLOCK_MONOTONIC")
-    CLOCK_MONOTONIC_RAW = rffi_platform.DefinedConstantInteger("CLOCK_MONOTONIC_RAW")
-    CLOCK_PROCESS_CPUTIME_ID = rffi_platform.DefinedConstantInteger("CLOCK_PROCESS_CPUTIME_ID")
-    CLOCK_THREAD_CPUTIME_ID = rffi_platform.DefinedConstantInteger("CLOCK_THREAD_CPUTIME_ID")
-
-cconfig = rffi_platform.configure(CConfig)
-
-HAS_CLOCK_GETTIME = cconfig["HAS_CLOCK_GETTIME"]
-
-CLOCK_REALTIME = cconfig["CLOCK_REALTIME"]
-CLOCK_MONOTONIC = cconfig["CLOCK_MONOTONIC"]
-CLOCK_MONOTONIC_RAW = cconfig["CLOCK_MONOTONIC_RAW"]
-CLOCK_PROCESS_CPUTIME_ID = cconfig["CLOCK_PROCESS_CPUTIME_ID"]
-CLOCK_THREAD_CPUTIME_ID = cconfig["CLOCK_THREAD_CPUTIME_ID"]
 
 if HAS_CLOCK_GETTIME:
-    #redo it for timespec
-    CConfig.TIMESPEC = rffi_platform.Struct("struct timespec", [
-        ("tv_sec", rffi.TIME_T),
-        ("tv_nsec", rffi.LONG),
-    ])
-    cconfig = rffi_platform.configure(CConfig)
-    TIMESPEC = cconfig['TIMESPEC']
-
-    c_clock_gettime = rffi.llexternal("clock_gettime",
-        [lltype.Signed, lltype.Ptr(TIMESPEC)], rffi.INT,
-        compilation_info=CConfig._compilation_info_, releasegil=False,
-        save_err=rffi.RFFI_SAVE_ERRNO
-    )
-    c_clock_getres = rffi.llexternal("clock_getres",
-        [lltype.Signed, lltype.Ptr(TIMESPEC)], rffi.INT,
-        compilation_info=CConfig._compilation_info_, releasegil=False,
-        save_err=rffi.RFFI_SAVE_ERRNO
-    )
 
     @unwrap_spec(clk_id="c_int")
     def clock_gettime(space, clk_id):
-        with lltype.scoped_alloc(TIMESPEC) as tp:
-            ret = c_clock_gettime(clk_id, tp)
+        with lltype.scoped_alloc(rtime.TIMESPEC) as tp:
+            ret = rtime.c_clock_gettime(clk_id, tp)
             if ret != 0:
                 raise exception_from_saved_errno(space, space.w_IOError)
-            return space.wrap(int(tp.c_tv_sec) + 1e-9 * int(tp.c_tv_nsec))
+            t = (float(rffi.getintfield(tp, 'c_tv_sec')) +
+                 float(rffi.getintfield(tp, 'c_tv_nsec')) * 0.000000001)
+        return space.wrap(t)
 
     @unwrap_spec(clk_id="c_int")
     def clock_getres(space, clk_id):
-        with lltype.scoped_alloc(TIMESPEC) as tp:
-            ret = c_clock_getres(clk_id, tp)
+        with lltype.scoped_alloc(rtime.TIMESPEC) as tp:
+            ret = rtime.c_clock_getres(clk_id, tp)
             if ret != 0:
                 raise exception_from_saved_errno(space, space.w_IOError)
-            return space.wrap(int(tp.c_tv_sec) + 1e-9 * int(tp.c_tv_nsec))
+            t = (float(rffi.getintfield(tp, 'c_tv_sec')) +
+                 float(rffi.getintfield(tp, 'c_tv_nsec')) * 0.000000001)
+        return space.wrap(t)
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
@@ -1,22 +1,38 @@
+import sys
 from pypy.interpreter.mixedmodule import MixedModule
 
+
 class Module(MixedModule):
     appleveldefs = {
     }
 
     interpleveldefs = {
-        'enable': 'interp_faulthandler.enable',
-        'disable': 'interp_faulthandler.disable',
-        'is_enabled': 'interp_faulthandler.is_enabled',
-        'register': 'interp_faulthandler.register',
+        'enable': 'handler.enable',
+        'disable': 'handler.disable',
+        'is_enabled': 'handler.is_enabled',
+#        'register': 'interp_faulthandler.register',
+#
+        'dump_traceback': 'handler.dump_traceback',
+#
+        '_read_null': 'handler.read_null',
+        '_sigsegv': 'handler.sigsegv',
+        '_sigfpe': 'handler.sigfpe',
+        '_sigabrt': 'handler.sigabrt',
+        '_stack_overflow': 'handler.stack_overflow',
+    }
 
-        'dump_traceback': 'interp_faulthandler.dump_traceback',
+    def setup_after_space_initialization(self):
+        """NOT_RPYTHON"""
+        if self.space.config.translation.thread:
+            self.extra_interpdef('dump_traceback_later',
+                                 'handler.dump_traceback_later')
+            self.extra_interpdef('cancel_dump_traceback_later',
+                                 'handler.cancel_dump_traceback_later')
+        if sys.platform != 'win32':
+            self.extra_interpdef('register', 'handler.register')
+            self.extra_interpdef('unregister', 'handler.unregister')
 
-        '_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',
-    }
+    def shutdown(self, space):
+        from pypy.module.faulthandler import handler
+        handler.finish(space)
+        MixedModule.shutdown(self, space)
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,99 @@
+import py
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr
+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')])
+
+eci_later = eci.merge(ExternalCompilationInfo(
+    pre_include_bits=['#define PYPY_FAULTHANDLER_LATER\n']))
+eci_user = eci.merge(ExternalCompilationInfo(
+    pre_include_bits=['#define PYPY_FAULTHANDLER_USER\n']))
+
+def direct_llexternal(*args, **kwargs):
+    kwargs.setdefault('_nowrapper', True)
+    kwargs.setdefault('compilation_info', eci)
+    return rffi.llexternal(*args, **kwargs)
+
+
+DUMP_CALLBACK = lltype.Ptr(lltype.FuncType(
+                     [rffi.INT, rffi.SIGNEDP, lltype.Signed], 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 = 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', [rffi.INT, rffi.CCHARP], lltype.Void)
+
+pypy_faulthandler_write_int = direct_llexternal(
+    'pypy_faulthandler_write_int', [rffi.INT, lltype.Signed], lltype.Void)
+
+pypy_faulthandler_dump_traceback = direct_llexternal(
+    'pypy_faulthandler_dump_traceback',
+    [rffi.INT, rffi.INT, llmemory.Address], lltype.Void)
+
+pypy_faulthandler_dump_traceback_later = direct_llexternal(
+    'pypy_faulthandler_dump_traceback_later',
+    [rffi.LONGLONG, rffi.INT, rffi.INT, rffi.INT], rffi.CCHARP,
+    compilation_info=eci_later)
+
+pypy_faulthandler_cancel_dump_traceback_later = direct_llexternal(
+    'pypy_faulthandler_cancel_dump_traceback_later', [], lltype.Void)
+
+pypy_faulthandler_check_signum = direct_llexternal(
+    'pypy_faulthandler_check_signum',
+    [rffi.LONG], rffi.INT,
+    compilation_info=eci_user)
+
+pypy_faulthandler_register = direct_llexternal(
+    'pypy_faulthandler_register',
+    [rffi.INT, rffi.INT, rffi.INT, rffi.INT], rffi.CCHARP,
+    compilation_info=eci_user)
+
+pypy_faulthandler_unregister = direct_llexternal(
+    'pypy_faulthandler_unregister',
+    [rffi.INT], rffi.INT,
+    compilation_info=eci_user)
+
+
+# for tests...
+
+pypy_faulthandler_read_null = direct_llexternal(
+    'pypy_faulthandler_read_null', [], lltype.Void)
+
+pypy_faulthandler_read_null_releasegil = direct_llexternal(
+    'pypy_faulthandler_read_null', [], lltype.Void,
+    _nowrapper=False, releasegil=True)
+
+pypy_faulthandler_sigsegv = direct_llexternal(
+    'pypy_faulthandler_sigsegv', [], lltype.Void)
+
+pypy_faulthandler_sigsegv_releasegil = direct_llexternal(
+    'pypy_faulthandler_sigsegv', [], lltype.Void,
+    _nowrapper=False, releasegil=True)
+
+pypy_faulthandler_sigfpe = direct_llexternal(
+    'pypy_faulthandler_sigfpe', [], lltype.Void)
+
+pypy_faulthandler_sigabrt = direct_llexternal(
+    'pypy_faulthandler_sigabrt', [], lltype.Void)
+
+pypy_faulthandler_stackoverflow = direct_llexternal(
+    'pypy_faulthandler_stackoverflow', [lltype.Float], lltype.Float)
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,54 @@
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rlib import rgc
+from rpython.rlib.rvmprof import traceback
+
+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
+
+
+MAX_STRING_LENGTH = 500
+
+global_buf = lltype.malloc(rffi.CCHARP.TO, MAX_STRING_LENGTH, flavor='raw',
+                           immortal=True, zero=True)
+
+def _dump(fd, s):
+    assert isinstance(s, str)
+    l = len(s)
+    if l >= MAX_STRING_LENGTH:
+        l = MAX_STRING_LENGTH - 1
+    i = 0
+    while i < l:
+        global_buf[i] = s[i]
+        i += 1
+    global_buf[l] = '\x00'
+    pypy_faulthandler_write(fd, global_buf)
+
+def _dump_int(fd, i):
+    pypy_faulthandler_write_int(fd, i)
+
+
+def dump_code(pycode, loc, fd):
+    if pycode is None:
+        _dump(fd, "  File ???")
+    else:
+        _dump(fd, '  File "')
+        _dump(fd, pycode.co_filename)
+        _dump(fd, '" in ')
+        _dump(fd, pycode.co_name)
+        _dump(fd, ", from line ")
+        _dump_int(fd, pycode.co_firstlineno)
+    if loc == traceback.LOC_JITTED:
+        _dump(fd, " [jitted]")
+    elif loc == traceback.LOC_JITTED_INLINED:
+        _dump(fd, " [jit inlined]")
+    _dump(fd, "\n")
+
+
+ at rgc.no_collect
+def _dump_callback(fd, array_p, array_length):
+    """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.
+    """
+    traceback.walk_traceback(PyCode, dump_code, fd, array_p, array_length)
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,20 +1,625 @@
+#include "faulthandler.h"
 #include <stdlib.h>
-#include "faulthandler.h"
+#include <stdio.h>
+#include <signal.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/resource.h>
+#include <math.h>
 
-int
-pypy_faulthandler_read_null(void)
+#ifdef RPYTHON_LL2CTYPES
+#  include "../../../rpython/rlib/rvmprof/src/rvmprof.h"
+#else
+#  include "common_header.h"
+#  include "structdef.h"
+#  include "rvmprof.h"
+#endif
+#include "src/threadlocal.h"
+
+#define MAX_FRAME_DEPTH   100
+#define FRAME_DEPTH_N     RVMPROF_TRACEBACK_ESTIMATE_N(MAX_FRAME_DEPTH)
+
+
+typedef struct sigaction _Py_sighandler_t;
+
+typedef struct {
+    const int signum;
+    volatile int enabled;
+    const char* name;
+    _Py_sighandler_t previous;
+} fault_handler_t;
+
+static struct {
+    int initialized;
+    int enabled;
+    volatile int fd, all_threads;
+    volatile pypy_faulthandler_cb_t dump_traceback;
+} 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
+void pypy_faulthandler_write(int fd, const char *str)
 {
-    volatile int *x;
-    volatile int y;
-
-    x = NULL;
-    y = *x;
-    return y;
+    (void)write(fd, str, strlen(str));
 }
 
-void
-pypy_faulthandler_sigsegv(void)
+RPY_EXTERN
+void pypy_faulthandler_write_int(int fd, long value)
 {
+    char buf[48];
+    sprintf(buf, "%ld", value);
+    pypy_faulthandler_write(fd, buf);
+}
+
+
+RPY_EXTERN
+void pypy_faulthandler_dump_traceback(int fd, int all_threads,
+                                      void *ucontext)
+{
+    pypy_faulthandler_cb_t fn;
+    intptr_t array_p[FRAME_DEPTH_N], array_length;
+
+    fn = fatal_error.dump_traceback;
+    if (!fn)
+        return;
+
+#ifndef RPYTHON_LL2CTYPES
+    if (all_threads && _RPython_ThreadLocals_AcquireTimeout(10000) == 0) {
+        /* This is known not to be perfectly safe against segfaults if we
+           don't hold the GIL ourselves.  Too bad.  I suspect that CPython
+           has issues there too.
+        */
+        struct pypy_threadlocal_s *my, *p;
+        int blankline = 0;
+        char buf[40];
+
+        my = (struct pypy_threadlocal_s *)_RPy_ThreadLocals_Get();
+        p = _RPython_ThreadLocals_Head();
+        p = _RPython_ThreadLocals_Enum(p);
+        while (p != NULL) {
+            if (blankline)
+                pypy_faulthandler_write(fd, "\n");
+            blankline = 1;
+
+            pypy_faulthandler_write(fd, my == p ? "Current thread" : "Thread");
+            sprintf(buf, " 0x%lx", (unsigned long)p->thread_ident);
+            pypy_faulthandler_write(fd, buf);
+            pypy_faulthandler_write(fd, " (most recent call first):\n");
+
+            array_length = vmprof_get_traceback(p->vmprof_tl_stack,
+                                                my == p ? ucontext : NULL,
+                                                array_p, FRAME_DEPTH_N);
+            fn(fd, array_p, array_length);
+
+            p = _RPython_ThreadLocals_Enum(p);
+        }
+        _RPython_ThreadLocals_Release();
+    }
+    else {
+        pypy_faulthandler_write(fd, "Stack (most recent call first):\n");
+        array_length = vmprof_get_traceback(NULL, ucontext,
+                                            array_p, FRAME_DEPTH_N);
+        fn(fd, array_p, array_length);
+    }
+#else
+    pypy_faulthandler_write(fd, "(no traceback when untranslated)\n");
+#endif
+}
+
+static void
+faulthandler_dump_traceback(int fd, int all_threads, void *ucontext)
+{
+    static volatile int reentrant = 0;
+
+    if (reentrant)
+        return;
+    reentrant = 1;
+    pypy_faulthandler_dump_traceback(fd, all_threads, ucontext);
+    reentrant = 0;
+}
+
+
+/************************************************************/
+
+
+#ifdef PYPY_FAULTHANDLER_LATER
+#include "src/thread.h"
+static struct {
+    int fd;
+    long long microseconds;
+    int repeat, exit;
+    /* The main thread always holds this lock. It is only released when
+       faulthandler_thread() is interrupted before this thread exits, or at
+       Python exit. */
+    struct RPyOpaque_ThreadLock cancel_event;
+    /* released by child thread when joined */
+    struct RPyOpaque_ThreadLock running;
+} thread_later;
+
+static void faulthandler_thread(void)
+{
+#ifndef _WIN32
+    /* we don't want to receive any signal */
+    sigset_t set;
+    sigfillset(&set);
+    pthread_sigmask(SIG_SETMASK, &set, NULL);
+#endif
+
+    RPyLockStatus st;
+    char buf[64];
+    unsigned long hour, minutes, seconds, fraction;
+    long long t;
+
+    do {
+        st = RPyThreadAcquireLockTimed(&thread_later.cancel_event,
+                                       thread_later.microseconds, 0);
+        if (st == RPY_LOCK_ACQUIRED) {
+            RPyThreadReleaseLock(&thread_later.cancel_event);
+            break;
+        }
+        /* Timeout => dump traceback */
+        assert(st == RPY_LOCK_FAILURE);
+
+        /* getting to know which thread holds the GIL is not as simple
+         * as in CPython, so for now we don't */
+
+        t = thread_later.microseconds;
+        fraction = (unsigned long)(t % 1000000);
+        t /= 1000000;
+        seconds = (unsigned long)(t % 60);
+        t /= 60;
+        minutes = (unsigned long)(t % 60);
+        t /= 60;
+        hour = (unsigned long)t;
+        if (fraction == 0)
+            sprintf(buf, "Timeout (%lu:%02lu:%02lu)!\n",
+                    hour, minutes, seconds);
+        else
+            sprintf(buf, "Timeout (%lu:%02lu:%02lu.%06lu)!\n",
+                    hour, minutes, seconds, fraction);
+
+        pypy_faulthandler_write(thread_later.fd, buf);
+        pypy_faulthandler_dump_traceback(thread_later.fd, 1, NULL);
+
+        if (thread_later.exit)
+            _exit(1);
+    } while (thread_later.repeat);
+
+    /* The only way out */
+    RPyThreadReleaseLock(&thread_later.running);
+}
+
+RPY_EXTERN
+char *pypy_faulthandler_dump_traceback_later(long long microseconds, int repeat,
+                                             int fd, int exit)
+{
+    pypy_faulthandler_cancel_dump_traceback_later();
+
+    thread_later.fd = fd;
+    thread_later.microseconds = microseconds;
+    thread_later.repeat = repeat;
+    thread_later.exit = exit;
+
+    RPyThreadAcquireLock(&thread_later.running, 1);
+
+    if (RPyThreadStart(&faulthandler_thread) == -1) {
+        RPyThreadReleaseLock(&thread_later.running);
+        return "unable to start watchdog thread";
+    }
+    return NULL;
+}
+#endif   /* PYPY_FAULTHANDLER_LATER */
+
+RPY_EXTERN
+void pypy_faulthandler_cancel_dump_traceback_later(void)
+{
+#ifdef PYPY_FAULTHANDLER_LATER
+    /* Notify cancellation */
+    RPyThreadReleaseLock(&thread_later.cancel_event);
+
+    /* Wait for thread to join (or does nothing if no thread is running) */
+    RPyThreadAcquireLock(&thread_later.running, 1);
+    RPyThreadReleaseLock(&thread_later.running);
+
+    /* The main thread should always hold the cancel_event lock */
+    RPyThreadAcquireLock(&thread_later.cancel_event, 1);
+#endif   /* PYPY_FAULTHANDLER_LATER */
+}
+
+
+/************************************************************/
+
+
+#ifdef PYPY_FAULTHANDLER_USER
+typedef struct {
+    int enabled;
+    int fd;
+    int all_threads;
+    int chain;
+    _Py_sighandler_t previous;
+} user_signal_t;
+
+static user_signal_t *user_signals;
+
+#ifndef NSIG
+# if defined(_NSIG)
+#  define NSIG _NSIG            /* For BSD/SysV */
+# elif defined(_SIGMAX)
+#  define NSIG (_SIGMAX + 1)    /* For QNX */
+# elif defined(SIGMAX)
+#  define NSIG (SIGMAX + 1)     /* For djgpp */
+# else
+#  define NSIG 64               /* Use a reasonable default value */
+# endif
+#endif
+
+static void faulthandler_user(int signum, siginfo_t *info, void *ucontext);
+
+static int
+faulthandler_register(int signum, int chain, _Py_sighandler_t *p_previous)
+{
+    struct sigaction action;
+    action.sa_handler = faulthandler_user;
+    sigemptyset(&action.sa_mask);
+    /* if the signal is received while the kernel is executing a system
+       call, try to restart the system call instead of interrupting it and
+       return EINTR. */
+    action.sa_flags = SA_RESTART | SA_SIGINFO;
+    if (chain) {
+        /* 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;
+    }
+    return sigaction(signum, &action, p_previous);
+}
+
+static void faulthandler_user(int signum, siginfo_t *info, void *ucontext)
+{
+    int save_errno;
+    user_signal_t *user = &user_signals[signum];
+
+    if (!user->enabled)
+        return;
+
+    save_errno = errno;
+    faulthandler_dump_traceback(user->fd, user->all_threads, ucontext);
+
+    if (user->chain) {
+        (void)sigaction(signum, &user->previous, NULL);
+        errno = save_errno;
+
+        /* call the previous signal handler */
+        raise(signum);
+
+        save_errno = errno;
+        (void)faulthandler_register(signum, user->chain, NULL);
+    }
+
+    errno = save_errno;
+}
+
+RPY_EXTERN
+int pypy_faulthandler_check_signum(long signum)
+{
+    unsigned int i;
+
+    for (i = 0; i < faulthandler_nsignals; i++) {
+        if (faulthandler_handlers[i].signum == signum) {
+            return -1;
+        }
+    }
+    if (signum < 1 || NSIG <= signum) {
+        return -2;
+    }
+    return 0;
+}
+
+RPY_EXTERN
+char *pypy_faulthandler_register(int signum, int fd, int all_threads, int chain)
+{
+    user_signal_t *user;
+    _Py_sighandler_t previous;
+    int err;
+
+    if (user_signals == NULL) {
+        user_signals = malloc(NSIG * sizeof(user_signal_t));
+        if (user_signals == NULL)
+            return "out of memory";
+        memset(user_signals, 0, NSIG * sizeof(user_signal_t));
+    }
+
+    user = &user_signals[signum];
+    user->fd = fd;
+    user->all_threads = all_threads;
+    user->chain = chain;
+
+    if (!user->enabled) {
+        err = faulthandler_register(signum, chain, &previous);
+        if (err)
+            return strerror(errno);
+
+        user->previous = previous;
+        user->enabled = 1;
+    }
+    return NULL;
+}
+
+RPY_EXTERN
+int pypy_faulthandler_unregister(int signum)
+{
+    user_signal_t *user;
+
+    if (user_signals == NULL)
+        return 0;
+
+    user = &user_signals[signum];
+    if (user->enabled) {
+        user->enabled = 0;
+        (void)sigaction(signum, &user->previous, NULL);
+        user->fd = -1;
+        return 1;
+    }
+    else
+        return 0;
+}
+#endif   /* PYPY_FAULTHANDLER_USER */
+
+
+/************************************************************/
+
+
+/* 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, siginfo_t *info, void *ucontext)
+{
+    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;
+    }
+    /* If not found, we use the SIGSEGV handler (the last one in the list) */
+
+    /* restore the previous handler */
+    if (handler->enabled) {
+        (void)sigaction(signum, &handler->previous, NULL);
+        handler->enabled = 0;
+    }
+
+    pypy_faulthandler_write(fd, "Fatal Python error: ");
+    pypy_faulthandler_write(fd, handler->name);
+    pypy_faulthandler_write(fd, "\n\n");
+
+    faulthandler_dump_traceback(fd, fatal_error.all_threads, ucontext);
+
+    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(pypy_faulthandler_cb_t dump_callback)
+{
+    if (fatal_error.initialized)
+        return NULL;
+    assert(!fatal_error.enabled);
+    fatal_error.dump_traceback = 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
+     * 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;
+        }
+    }
+
+#ifdef PYPY_FAULTHANDLER_LATER
+    if (!RPyThreadLockInit(&thread_later.cancel_event) ||
+        !RPyThreadLockInit(&thread_later.running))
+        return "failed to initialize locks";
+    RPyThreadAcquireLock(&thread_later.cancel_event, 1);
+#endif
+
+    fatal_error.fd = -1;
+    fatal_error.initialized = 1;
+
+    return NULL;
+}
+
+RPY_EXTERN
+void pypy_faulthandler_teardown(void)
+{
+    if (fatal_error.initialized) {
+
+#ifdef PYPY_FAULTHANDLER_LATER
+        pypy_faulthandler_cancel_dump_traceback_later();
+        RPyThreadReleaseLock(&thread_later.cancel_event);
+        RPyOpaqueDealloc_ThreadLock(&thread_later.running);
+        RPyOpaqueDealloc_ThreadLock(&thread_later.cancel_event);
+#endif
+
+#ifdef PYPY_FAULTHANDLER_USER
+        int signum;
+        for (signum = 0; signum < NSIG; signum++)
+            pypy_faulthandler_unregister(signum);
+        /* don't free 'user_signals', the gain is very minor and it can
+           lead to rare crashes if another thread is still busy */
+#endif
+
+        pypy_faulthandler_disable();
+        fatal_error.initialized = 0;
+        if (stack.ss_sp) {
+            stack.ss_flags = SS_DISABLE;
+            sigaltstack(&stack, NULL);
+            free(stack.ss_sp);
+            stack.ss_sp = NULL;
+        }
+    }
+}
+
+RPY_EXTERN
+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) {
+        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_sigaction = 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 | SA_SIGINFO;
+            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 strerror(errno);
+            }
+            handler->enabled = 1;
+        }
+    }
+    return NULL;
+}
+
+RPY_EXTERN
+void pypy_faulthandler_disable(void)
+{
+    int i;
+    if (fatal_error.enabled) {
+        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;
+        }
+    }
+    fatal_error.fd = -1;
+}
+
+RPY_EXTERN
+int pypy_faulthandler_is_enabled(void)
+{
+    return fatal_error.enabled;
+}
+
+
+/************************************************************/
+
+
+/* for tests... */
+
+static void
+faulthandler_suppress_crash_report(void)
+{
+#ifdef MS_WINDOWS
+    UINT mode;
+
+    /* Configure Windows to not display the Windows Error Reporting dialog */
+    mode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
+    SetErrorMode(mode | SEM_NOGPFAULTERRORBOX);
+#endif
+
+#ifndef MS_WINDOWS
+    struct rlimit rl;
+
+    /* Disable creation of core dump */
+    if (getrlimit(RLIMIT_CORE, &rl) != 0) {
+        rl.rlim_cur = 0;
+        setrlimit(RLIMIT_CORE, &rl);
+    }
+#endif
+
+#ifdef _MSC_VER
+    /* Visual Studio: configure abort() to not display an error message nor
+       open a popup asking to report the fault. */
+    _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
+#endif
+}
+
+RPY_EXTERN
+int pypy_faulthandler_read_null(void)
+{
+    int *volatile x;
+
+    faulthandler_suppress_crash_report();
+    x = NULL;
+    return *x;
+}
+
+RPY_EXTERN
+void pypy_faulthandler_sigsegv(void)
+{
+    faulthandler_suppress_crash_report();
 #if defined(MS_WINDOWS)
     /* For SIGSEGV, faulthandler_fatal_error() restores the previous signal
        handler and then gives back the execution flow to the program (without
@@ -34,12 +639,13 @@
 #endif
 }
 
-int
-pypy_faulthandler_sigfpe(void)
+RPY_EXTERN
+int pypy_faulthandler_sigfpe(void)
 {
     /* Do an integer division by zero: raise a SIGFPE on Intel CPU, but not on
        PowerPC. Use volatile to disable compile-time optimizations. */
     volatile int x = 1, y = 0, z;
+    faulthandler_suppress_crash_report();
     z = x / y;
     /* If the division by zero didn't raise a SIGFPE (e.g. on PowerPC),
        raise it manually. */
@@ -49,29 +655,25 @@
     return z;
 }
 
-void
-pypy_faulthandler_sigabrt()
+RPY_EXTERN
+void pypy_faulthandler_sigabrt(void)
 {
-#ifdef _MSC_VER
-    /* Visual Studio: configure abort() to not display an error message nor
-       open a popup asking to report the fault. */
-    _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
-#endif
+    faulthandler_suppress_crash_report();
     abort();
 }
 
-#ifdef SIGBUS
-void
-pypy_faulthandler_sigbus(void)
+static double fh_stack_overflow(double levels)
 {
-    raise(SIGBUS);
+    if (levels > 2.5) {
+        return (sqrt(fh_stack_overflow(levels - 1.0))
+                + fh_stack_overflow(levels * 1e-10));
+    }
+    return 1e100 + levels;
 }
-#endif
 
-#ifdef SIGILL
-void
-pypy_faulthandler_sigill(void)
+RPY_EXTERN
+double pypy_faulthandler_stackoverflow(double levels)
 {
-    raise(SIGILL);
+    faulthandler_suppress_crash_report();
+    return fh_stack_overflow(levels);
 }
-#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,19 +1,40 @@
 #ifndef PYPY_FAULTHANDLER_H
 #define PYPY_FAULTHANDLER_H
 
-#include <signal.h>
 #include "src/precommondefs.h"
+#include <stdint.h>
+
+
+typedef void (*pypy_faulthandler_cb_t)(int fd, intptr_t *array_p,
+                                       intptr_t length);
+
+RPY_EXTERN char *pypy_faulthandler_setup(pypy_faulthandler_cb_t dump_callback);
+RPY_EXTERN void pypy_faulthandler_teardown(void);
+
+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(int fd, const char *str);
+RPY_EXTERN void pypy_faulthandler_write_int(int fd, long value);
+
+RPY_EXTERN void pypy_faulthandler_dump_traceback(int fd, int all_threads,
+                                                 void *ucontext);
+
+RPY_EXTERN char *pypy_faulthandler_dump_traceback_later(
+    long long microseconds, int repeat, int fd, int exit);
+RPY_EXTERN void pypy_faulthandler_cancel_dump_traceback_later(void);
+
+RPY_EXTERN int pypy_faulthandler_check_signum(long signum);
+RPY_EXTERN char *pypy_faulthandler_register(int, int, int, int);
+RPY_EXTERN int pypy_faulthandler_unregister(int signum);
+
 
 RPY_EXTERN int pypy_faulthandler_read_null(void);
 RPY_EXTERN void pypy_faulthandler_sigsegv(void);
 RPY_EXTERN int pypy_faulthandler_sigfpe(void);
-RPY_EXTERN void pypy_faulthandler_sigabrt();
-#ifdef SIGBUS
-RPY_EXTERN void pypy_faulthandler_sigbus(void);
-#endif
+RPY_EXTERN void pypy_faulthandler_sigabrt(void);
+RPY_EXTERN double pypy_faulthandler_stackoverflow(double);
 
-#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,209 @@
+import os
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+from rpython.rlib.rposix import is_valid_fd
+from rpython.rlib.rarithmetic import widen, ovfcheck_float_to_longlong
+from rpython.rlib.objectmodel import keepalive_until_here
+from rpython.rtyper.annlowlevel import llhelper
+
+from pypy.interpreter.error import OperationError, oefmt
+from pypy.interpreter.error import exception_from_saved_errno
+from pypy.interpreter.gateway import unwrap_spec
+from pypy.module.faulthandler import cintf, dumper
+
+
+class Handler(object):
+    def __init__(self, space):
+        "NOT_RPYTHON"
+        self.space = space
+        self._cleanup_()
+
+    def _cleanup_(self):
+        self.fatal_error_w_file = None
+        self.dump_traceback_later_w_file = None
+        self.user_w_files = 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.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.c_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 setup(self):
+        dump_callback = llhelper(cintf.DUMP_CALLBACK, dumper._dump_callback)
+        self.check_err(cintf.pypy_faulthandler_setup(dump_callback))
+
+    def enable(self, w_file, all_threads):
+        fileno, w_file = self.get_fileno_and_file(w_file)
+        self.setup()
+        self.fatal_error_w_file = w_file
+        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 bool(widen(cintf.pypy_faulthandler_is_enabled()))
+
+    def dump_traceback(self, w_file, all_threads):
+        fileno, w_file = self.get_fileno_and_file(w_file)
+        self.setup()
+        cintf.pypy_faulthandler_dump_traceback(
+            rffi.cast(rffi.INT, fileno),
+            rffi.cast(rffi.INT, all_threads),
+            llmemory.NULL)
+        keepalive_until_here(w_file)
+
+    def dump_traceback_later(self, timeout, repeat, w_file, exit):
+        space = self.space
+        timeout *= 1e6
+        try:
+            microseconds = ovfcheck_float_to_longlong(timeout)
+        except OverflowError:
+            raise oefmt(space.w_OverflowError, "timeout value is too large")
+        if microseconds <= 0:
+            raise oefmt(space.w_ValueError, "timeout must be greater than 0")
+        fileno, w_file = self.get_fileno_and_file(w_file)
+        self.setup()
+        self.check_err(cintf.pypy_faulthandler_dump_traceback_later(
+            rffi.cast(rffi.LONGLONG, microseconds),
+            rffi.cast(rffi.INT, repeat),
+            rffi.cast(rffi.INT, fileno),
+            rffi.cast(rffi.INT, exit)))
+        self.dump_traceback_later_w_file = w_file
+
+    def cancel_dump_traceback_later(self):
+        cintf.pypy_faulthandler_cancel_dump_traceback_later()
+        self.dump_traceback_later_w_file = None
+
+    def check_signum(self, signum):
+        err = rffi.cast(lltype.Signed,
+                        cintf.pypy_faulthandler_check_signum(signum))
+        if err < 0:
+            space = self.space
+            if err == -1:
+                raise oefmt(space.w_RuntimeError,
+                            "signal %d cannot be registered, "
+                            "use enable() instead", signum)
+            else:
+                raise oefmt(space.w_ValueError, "signal number out of range")
+
+    def register(self, signum, w_file, all_threads, chain):
+        self.check_signum(signum)
+        fileno, w_file = self.get_fileno_and_file(w_file)
+        self.setup()
+        self.check_err(cintf.pypy_faulthandler_register(
+            rffi.cast(rffi.INT, signum),
+            rffi.cast(rffi.INT, fileno),
+            rffi.cast(rffi.INT, all_threads),
+            rffi.cast(rffi.INT, chain)))
+        if self.user_w_files is None:
+            self.user_w_files = {}
+        self.user_w_files[signum] = w_file
+
+    def unregister(self, signum):
+        self.check_signum(signum)
+        change = cintf.pypy_faulthandler_unregister(
+            rffi.cast(rffi.INT, signum))
+        if self.user_w_files is not None:
+            self.user_w_files.pop(signum, None)
+        return rffi.cast(lltype.Signed, change) == 1
+
+    def finish(self):
+        cintf.pypy_faulthandler_teardown()
+        self._cleanup_()
+
+
+def finish(space):
+    "Finalize the faulthandler logic (called from shutdown())"
+    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())
+
+ at unwrap_spec(all_threads=int)
+def dump_traceback(space, w_file=None, all_threads=0):
+    """dump the traceback of the current thread into file
+    including all threads if all_threads is True"""
+    space.fromcache(Handler).dump_traceback(w_file, all_threads)
+
+ at unwrap_spec(timeout=float, repeat=int, exit=int)
+def dump_traceback_later(space, timeout, repeat=0, w_file=None, exit=0):
+    """dump the traceback of all threads in timeout seconds,
+    or each timeout seconds if repeat is True. If exit is True,
+    call _exit(1) which is not safe."""
+    space.fromcache(Handler).dump_traceback_later(timeout, repeat, w_file, exit)
+
+def cancel_dump_traceback_later(space):
+    """cancel the previous call to dump_traceback_later()."""
+    space.fromcache(Handler).cancel_dump_traceback_later()
+
+ at unwrap_spec(signum=int, all_threads=int, chain=int)
+def register(space, signum, w_file=None, all_threads=1, chain=0):
+    space.fromcache(Handler).register(signum, w_file, all_threads, chain)
+
+ at unwrap_spec(signum=int)
+def unregister(space, signum):
+    return space.wrap(space.fromcache(Handler).unregister(signum))
+
+
+# for tests...
+
+ at unwrap_spec(release_gil=int)
+def read_null(space, release_gil=0):
+    if release_gil:
+        cintf.pypy_faulthandler_read_null_releasegil()
+    else:
+        cintf.pypy_faulthandler_read_null()
+
+ at unwrap_spec(release_gil=int)
+def sigsegv(space, release_gil=0):
+    if release_gil:
+        cintf.pypy_faulthandler_sigsegv_releasegil()
+    else:
+        cintf.pypy_faulthandler_sigsegv()
+
+def sigfpe(space):
+    cintf.pypy_faulthandler_sigfpe()
+
+def sigabrt(space):
+    cintf.pypy_faulthandler_sigabrt()
+
+ at unwrap_spec(levels=int)
+def stack_overflow(space, levels=100000000):
+    levels = float(levels)
+    return space.wrap(cintf.pypy_faulthandler_stackoverflow(levels))
diff --git a/pypy/module/faulthandler/interp_faulthandler.py b/pypy/module/faulthandler/interp_faulthandler.py
deleted file mode 100644
--- a/pypy/module/faulthandler/interp_faulthandler.py
+++ /dev/null
@@ -1,127 +0,0 @@
-import os
-import py
-
-from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
-from rpython.rtyper.lltypesystem import lltype, rffi
-from rpython.translator import cdir
-from rpython.translator.tool.cbuild import ExternalCompilationInfo
-from pypy.interpreter.error import OperationError, oefmt
-
-MAX_NTHREADS = 100
-
-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_read_null = llexternal(
-    'pypy_faulthandler_read_null', [], lltype.Void)
-pypy_faulthandler_read_null_nogil = llexternal(
-    'pypy_faulthandler_read_null', [], lltype.Void,
-    releasegil=True)
-pypy_faulthandler_sigsegv = llexternal(
-    'pypy_faulthandler_sigsegv', [], lltype.Void)
-pypy_faulthandler_sigfpe = llexternal(
-    'pypy_faulthandler_sigfpe', [], lltype.Void)
-pypy_faulthandler_sigabrt = llexternal(
-    'pypy_faulthandler_sigabrt', [], lltype.Void)
-pypy_faulthandler_sigbus = llexternal(
-    'pypy_faulthandler_sigbus', [], lltype.Void)
-pypy_faulthandler_sigill = llexternal(
-    'pypy_faulthandler_sigill', [], lltype.Void)
-
-class FatalErrorState(object):
-    def __init__(self, space):
-        self.enabled = False
-        self.all_threads = True
-
- at unwrap_spec(w_file=WrappedDefault(None),
-             w_all_threads=WrappedDefault(True))
-def enable(space, w_file, w_all_threads):
-    state = space.fromcache(FatalErrorState)
-    state.enabled = True
-    state.all_threads = bool(space.int_w(w_all_threads))
-
-def disable(space):
-    state = space.fromcache(FatalErrorState)
-    state.enabled = False
-
-def is_enabled(space):
-    return space.wrap(space.fromcache(FatalErrorState).enabled)
-
-def register(space, __args__):
-    pass
-
-
- at unwrap_spec(w_file=WrappedDefault(None),
-             w_all_threads=WrappedDefault(True))
-def dump_traceback(space, w_file, w_all_threads):
-    current_ec = space.getexecutioncontext()
-    if space.int_w(w_all_threads):
-        ecs = space.threadlocals.getallvalues()
-    else:
-        ecs = {0: current_ec}
-
-    if space.is_none(w_file):
-        w_file = space.sys.get('stderr')
-    fd = space.c_filedescriptor_w(w_file)
-
-    nthreads = 0
-    for thread_ident, ec in ecs.items():
-        if nthreads:
-            os.write(fd, "\n")
-        if nthreads >= MAX_NTHREADS:
-            os.write(fd, "...\n")
-            break
-        if ec is current_ec:
-            os.write(fd, "Current thread 0x%x:\n" % thread_ident)
-        else:
-            os.write(fd, "Thread 0x%x:\n" % thread_ident)
-
-        frame = ec.gettopframe()
-        while frame:
-            code = frame.pycode
-            lineno = frame.get_last_lineno()
-            if code:
-                os.write(fd, "  File \"%s\", line %s in %s\n" % (
-                        code.co_filename, lineno, code.co_name))
-            else:
-                os.write(fd, "  File ???, line %s in ???\n" % (
-                        lineno,))
-
-            frame = frame.f_backref()
- 
-
- at unwrap_spec(w_release_gil=WrappedDefault(False))
-def read_null(space, w_release_gil):
-    if space.is_true(w_release_gil):
-        pypy_faulthandler_read_null_nogil()
-    else:
-        pypy_faulthandler_read_null()
-
-def sigsegv():
-    pypy_faulthandler_sigsegv()
-
-def sigfpe():
-    pypy_faulthandler_sigfpe()
-
-def sigabrt():
-    pypy_faulthandler_sigabrt()
-
-#def sigbus():
-#    pypy_faulthandler_sigbus()
-
-#def sigill():
-#    pypy_faulthandler_sigill()
-
- at unwrap_spec(msg=str)
-def fatal_error(space, msg):
-    os.write(2, "Fatal Python error: %s\n" % msg);
-    dump_traceback(space, space.wrap(None), space.wrap(True))
-    raise RuntimeError(msg)
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
@@ -1,12 +1,7 @@
-from pypy.module.faulthandler import interp_faulthandler
-
-class TestFaultHandler:
-    def test_fatal_error(self, space):
-        raises(RuntimeError, interp_faulthandler.fatal_error, space, "Message")
 
 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/pypy/module/time/__init__.py b/pypy/module/time/__init__.py
--- a/pypy/module/time/__init__.py
+++ b/pypy/module/time/__init__.py
@@ -1,7 +1,7 @@
 
 from pypy.interpreter.mixedmodule import MixedModule
-from .interp_time import (CLOCK_CONSTANTS, HAS_CLOCK_GETTIME, cConfig,
-                          HAS_MONOTONIC)
+from .interp_time import HAS_MONOTONIC
+from rpython.rlib import rtime
 import os
 
 _WIN = os.name == "nt"
@@ -24,20 +24,18 @@
         'process_time': 'interp_time.process_time',
     }
 
-    if HAS_CLOCK_GETTIME:
+    if rtime.HAS_CLOCK_GETTIME:
         interpleveldefs['clock_gettime'] = 'interp_time.clock_gettime'
         interpleveldefs['clock_settime'] = 'interp_time.clock_settime'
         interpleveldefs['clock_getres'] = 'interp_time.clock_getres'
+        for constant in rtime.ALL_DEFINED_CLOCKS:
+            interpleveldefs[constant] = 'space.wrap(%d)' % (
+                getattr(rtime, constant),)
     if HAS_MONOTONIC:
         interpleveldefs['monotonic'] = 'interp_time.monotonic'
     if os.name == "posix":
         interpleveldefs['tzset'] = 'interp_time.tzset'
 
-    for constant in CLOCK_CONSTANTS:
-        value = getattr(cConfig, constant)
-        if value is not None:
-            interpleveldefs[constant] = 'space.wrap(interp_time.cConfig.%s)' % constant
-
     appleveldefs = {
         'struct_time': 'app_time.struct_time',
         '__doc__': 'app_time.__doc__',
diff --git a/pypy/module/time/interp_time.py b/pypy/module/time/interp_time.py
--- a/pypy/module/time/interp_time.py
+++ b/pypy/module/time/interp_time.py
@@ -160,15 +160,6 @@
     )
     CLOCKS_PER_SEC = platform.ConstantInteger("CLOCKS_PER_SEC")
     has_gettimeofday = platform.Has('gettimeofday')
-    has_clock_gettime = platform.Has('clock_gettime')
-    CLOCK_PROF = platform.DefinedConstantInteger('CLOCK_PROF')
-
-CLOCK_CONSTANTS = ['CLOCK_HIGHRES', 'CLOCK_MONOTONIC', 'CLOCK_MONOTONIC_RAW',
-                   'CLOCK_PROCESS_CPUTIME_ID', 'CLOCK_REALTIME',
-                   'CLOCK_THREAD_CPUTIME_ID']
-
-for constant in CLOCK_CONSTANTS:
-    setattr(CConfig, constant, platform.DefinedConstantInteger(constant))
 
 if _POSIX:
     calling_conv = 'c'
@@ -227,9 +218,9 @@
     timeval = cConfig.timeval
 
 CLOCKS_PER_SEC = cConfig.CLOCKS_PER_SEC
-HAS_CLOCK_GETTIME = cConfig.has_clock_gettime
-HAS_CLOCK_HIGHRES = cConfig.CLOCK_HIGHRES is not None
-HAS_CLOCK_MONOTONIC = cConfig.CLOCK_MONOTONIC is not None
+HAS_CLOCK_GETTIME = rtime.HAS_CLOCK_GETTIME
+HAS_CLOCK_HIGHRES = rtime.CLOCK_HIGHRES is not None
+HAS_CLOCK_MONOTONIC = rtime.CLOCK_MONOTONIC is not None
 HAS_MONOTONIC = (_WIN or _MACOSX or
                  (HAS_CLOCK_GETTIME and (HAS_CLOCK_HIGHRES or HAS_CLOCK_MONOTONIC)))
 tm = cConfig.tm
@@ -316,12 +307,7 @@
                        save_err=rffi.RFFI_SAVE_ERRNO)
 if HAS_CLOCK_GETTIME:
     from rpython.rlib.rtime import TIMESPEC, c_clock_gettime
-    c_clock_settime = external('clock_settime',
-                               [lltype.Signed, lltype.Ptr(TIMESPEC)], rffi.INT,
-                               save_err=rffi.RFFI_SAVE_ERRNO)
-    c_clock_getres = external('clock_getres',
-                              [lltype.Signed, lltype.Ptr(TIMESPEC)], rffi.INT,
-                              save_err=rffi.RFFI_SAVE_ERRNO)
+    from rpython.rlib.rtime import c_clock_settime, c_clock_getres
 if _POSIX:
     c_tzset = external('tzset', [], lltype.Void)
 if _WIN:
@@ -623,11 +609,11 @@
     Fractions of a second may be present if the system clock provides them."""
     if HAS_CLOCK_GETTIME:
         with lltype.scoped_alloc(TIMESPEC) as timespec:
-            ret = c_clock_gettime(cConfig.CLOCK_REALTIME, timespec)
+            ret = c_clock_gettime(rtime.CLOCK_REALTIME, timespec)
             if ret == 0:
                 if w_info is not None:
                     with lltype.scoped_alloc(TIMESPEC) as tsres:
-                        ret = c_clock_getres(cConfig.CLOCK_REALTIME, tsres)
+                        ret = c_clock_getres(rtime.CLOCK_REALTIME, tsres)
                         if ret == 0:
                             res = _timespec_to_seconds(tsres)
                         else:
@@ -903,11 +889,11 @@
     else:
         assert _POSIX
         def monotonic(space, w_info=None):
-            if cConfig.CLOCK_HIGHRES is not None:
-                clk_id = cConfig.CLOCK_HIGHRES
+            if rtime.CLOCK_HIGHRES is not None:
+                clk_id = rtime.CLOCK_HIGHRES
                 implementation = "clock_gettime(CLOCK_HIGHRES)"
             else:
-                clk_id = cConfig.CLOCK_MONOTONIC
+                clk_id = rtime.CLOCK_MONOTONIC
                 implementation = "clock_gettime(CLOCK_MONOTONIC)"
             w_result = clock_gettime(space, clk_id)
             if w_info is not None:
@@ -994,13 +980,13 @@
 
     def process_time(space, w_info=None):
         if HAS_CLOCK_GETTIME and (
-                cConfig.CLOCK_PROF is not None or
-                cConfig.CLOCK_PROCESS_CPUTIME_ID is not None):
-            if cConfig.CLOCK_PROF is not None:
-                clk_id = cConfig.CLOCK_PROF
+                rtime.CLOCK_PROF is not None or
+                rtime.CLOCK_PROCESS_CPUTIME_ID is not None):
+            if rtime.CLOCK_PROF is not None:
+                clk_id = rtime.CLOCK_PROF
                 implementation = "clock_gettime(CLOCK_PROF)"
             else:
-                clk_id = cConfig.CLOCK_PROCESS_CPUTIME_ID
+                clk_id = rtime.CLOCK_PROCESS_CPUTIME_ID
                 implementation = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)"
             with lltype.scoped_alloc(TIMESPEC) as timespec:
                 ret = c_clock_gettime(clk_id, timespec)
diff --git a/pypy/objspace/std/test/test_bytesobject.py b/pypy/objspace/std/test/test_bytesobject.py
--- a/pypy/objspace/std/test/test_bytesobject.py
+++ b/pypy/objspace/std/test/test_bytesobject.py
@@ -256,19 +256,13 @@
         assert b'one'.replace(memoryview(b'o'), memoryview(b'n')) == b'nne'
 
     def test_strip(self):
-        s = " a b "
-        assert s.strip() == "a b"
-        assert s.rstrip() == " a b"
-        assert s.lstrip() == "a b "
+        s = b" a b "
+        assert s.strip() == b"a b"
+        assert s.rstrip() == b" a b"
+        assert s.lstrip() == b"a b "
         assert b'xyzzyhelloxyzzy'.strip(b'xyz') == b'hello'
         assert b'xyzzyhelloxyzzy'.lstrip(b'xyz') == b'helloxyzzy'
         assert b'xyzzyhelloxyzzy'.rstrip(b'xyz') == b'xyzzyhello'
-        exc = raises(TypeError, s.strip, buffer(' '))
-        assert str(exc.value) == 'strip arg must be None, str or unicode'
-        exc = raises(TypeError, s.rstrip, buffer(' '))
-        assert str(exc.value) == 'strip arg must be None, str or unicode'
-        exc = raises(TypeError, s.lstrip, buffer(' '))
-        assert str(exc.value) == 'strip arg must be None, str or unicode'
 
     def test_zfill(self):
         assert b'123'.zfill(2) == b'123'
diff --git a/pypy/tool/build_cffi_imports.py b/pypy/tool/build_cffi_imports.py
--- a/pypy/tool/build_cffi_imports.py
+++ b/pypy/tool/build_cffi_imports.py
@@ -1,5 +1,5 @@
-import sys, shutil
-from rpython.tool.runsubprocess import run_subprocess
+from __future__ import print_function
+import sys, shutil, os
 
 class MissingDependenciesError(Exception):
     pass
@@ -20,6 +20,8 @@
     }
 
 def create_cffi_import_libraries(pypy_c, options, basedir):
+    from rpython.tool.runsubprocess import run_subprocess
+
     shutil.rmtree(str(basedir.join('lib_pypy', '__pycache__')),
                   ignore_errors=True)
     failures = []
@@ -32,11 +34,11 @@
         else:
             args = ['-c', 'import ' + module]
             cwd = None
-        print >> sys.stderr, '*', ' '.join(args)
+        print('*', ' '.join(args), file=sys.stderr)
         try:
             status, stdout, stderr = run_subprocess(str(pypy_c), args, cwd=cwd)
             if status != 0:
-                print >> sys.stderr, stdout, stderr
+                print(stdout, stderr, file=sys.stderr)
                 failures.append((key, module))
         except:
             import traceback;traceback.print_exc()
@@ -44,11 +46,16 @@
     return failures
 
 if __name__ == '__main__':
-    import py, os
     if '__pypy__' not in sys.builtin_module_names:
-        print >> sys.stderr, 'Call with a pypy interpreter'
+        print('Call with a pypy interpreter', file=sys.stderr)
         sys.exit(1)
 
+    tool_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
+    base_dir = os.path.dirname(os.path.dirname(tool_dir))
+    sys.path.insert(0, base_dir)
+
+    import py
+
     class Options(object):
         pass
 
@@ -63,20 +70,20 @@
     options = Options()
     failures = create_cffi_import_libraries(exename, options, basedir)
     if len(failures) > 0:
-        print >> sys.stderr, '*** failed to build the CFFI modules %r' % (
-            [f[1] for f in failures],)
-        print >> sys.stderr, '''
+        print('*** failed to build the CFFI modules %r' % (
+            [f[1] for f in failures],), file=sys.stderr)
+        print('''
 PyPy can still be used as long as you don't need the corresponding
 modules.  If you do need them, please install the missing headers and
 libraries (see error messages just above) and then re-run the command:
 
     %s %s
-''' % (sys.executable, ' '.join(sys.argv))
+''' % (sys.executable, ' '.join(sys.argv)), file=sys.stderr)
         sys.exit(1)
 
     if len(sys.argv) > 1 and sys.argv[1] == '--test':
         # monkey patch a failure, just to test
-        print >> sys.stderr, 'This line should be followed by a traceback'
+        print('This line should be followed by a traceback', file=sys.stderr)
         for k in cffi_build_scripts:
             setattr(options, 'no_' + k, True)
         must_fail = '_missing_build_script.py'
diff --git a/rpython/jit/backend/llsupport/test/test_gc_integration.py b/rpython/jit/backend/llsupport/test/test_gc_integration.py
--- a/rpython/jit/backend/llsupport/test/test_gc_integration.py
+++ b/rpython/jit/backend/llsupport/test/test_gc_integration.py
@@ -323,13 +323,10 @@
     def test_malloc_slowpath(self):
         def check(frame):
             expected_size = 1
-            idx = 0
             fixed_size = self.cpu.JITFRAME_FIXED_SIZE
             if self.cpu.backend_name.startswith('arm'):
                 # jitframe fixed part is larger here
                 expected_size = 2
-                idx = 1
-                fixed_size -= 32
             if self.cpu.backend_name.startswith('zarch') or \
                self.cpu.backend_name.startswith('ppc'):
                 # the allocation always allocates the register
@@ -342,7 +339,10 @@
             # registers (p0 and p1 are moved away when doing p2, but not
             # spilled, just moved to different registers)
             bits = [n for n in range(fixed_size)
-                      if frame.jf_gcmap[idx] & (1<<n)]
+                      if frame.jf_gcmap[0] & (1<<n)]
+            if expected_size > 1:
+                bits += [n for n in range(32, fixed_size)
+                           if frame.jf_gcmap[1] & (1<<(n - 32))]
             assert len(bits) == 2
 
         self.cpu = self.getcpu(check)
diff --git a/rpython/rlib/rtime.py b/rpython/rlib/rtime.py
--- a/rpython/rlib/rtime.py
+++ b/rpython/rlib/rtime.py
@@ -67,10 +67,22 @@
         includes=['time.h'],
         libraries=libraries
     )
+    _NO_MISSING_RT = rffi_platform.Has('printf("%d", clock_gettime(0, 0))')
     TIMESPEC = rffi_platform.Struct('struct timespec', [('tv_sec', rffi.LONG),
                                                         ('tv_nsec', rffi.LONG)])
 
-constant_names = ['RUSAGE_SELF', 'EINTR', 'CLOCK_PROCESS_CPUTIME_ID']
+constant_names = ['RUSAGE_SELF', 'EINTR',
+                  'CLOCK_REALTIME',
+                  'CLOCK_REALTIME_COARSE',
+                  'CLOCK_MONOTONIC',
+                  'CLOCK_MONOTONIC_COARSE',
+                  'CLOCK_MONOTONIC_RAW',
+                  'CLOCK_BOOTTIME',
+                  'CLOCK_PROCESS_CPUTIME_ID',
+                  'CLOCK_THREAD_CPUTIME_ID',
+                  'CLOCK_HIGHRES',
+                  'CLOCK_PROF',
+]
 for const in constant_names:
     setattr(CConfig, const, rffi_platform.DefinedConstantInteger(const))
 defs_names = ['GETTIMEOFDAY_NO_TZ']
@@ -158,16 +170,40 @@
         divisor = 0.0
         counter_start = 0
     state = State()
-elif CLOCK_PROCESS_CPUTIME_ID is not None:
+
+HAS_CLOCK_GETTIME = (CLOCK_MONOTONIC is not None)
+if HAS_CLOCK_GETTIME:
     # Linux and other POSIX systems with clock_gettime()
+    # TIMESPEC:
     globals().update(rffi_platform.configure(CConfigForClockGetTime))
-    TIMESPEC = TIMESPEC
-    CLOCK_PROCESS_CPUTIME_ID = CLOCK_PROCESS_CPUTIME_ID
-    eci_with_lrt = eci.merge(ExternalCompilationInfo(libraries=['rt']))
+    # do we need to add -lrt?
+    eciclock = CConfigForClockGetTime._compilation_info_
+    if not _NO_MISSING_RT:
+        eciclock = eciclock.merge(ExternalCompilationInfo(libraries=['rt']))
+    # the functions:
+    c_clock_getres = external("clock_getres",
+                              [lltype.Signed, lltype.Ptr(TIMESPEC)],
+                              rffi.INT, releasegil=False,
+                              save_err=rffi.RFFI_SAVE_ERRNO,
+                              compilation_info=eciclock)
     c_clock_gettime = external('clock_gettime',
                                [lltype.Signed, lltype.Ptr(TIMESPEC)],
                                rffi.INT, releasegil=False,
-                               compilation_info=eci_with_lrt)
+                               save_err=rffi.RFFI_SAVE_ERRNO,
+                               compilation_info=eciclock)
+    c_clock_settime = external('clock_settime',
+                               [lltype.Signed, lltype.Ptr(TIMESPEC)],
+                               rffi.INT, releasegil=False,
+                               save_err=rffi.RFFI_SAVE_ERRNO,
+                               compilation_info=eciclock)
+    # Note: there is no higher-level functions here to access
+    # clock_gettime().  The issue is that we'd need a way that keeps
+    # nanosecond precision, depending on the usage, so we can't have a
+    # nice function that returns the time as a float.
+    ALL_DEFINED_CLOCKS = [const for const in constant_names
+                          if const.startswith('CLOCK_')
+                             and globals()[const] is not None]
+
 if need_rusage:
     RUSAGE = RUSAGE
     RUSAGE_SELF = RUSAGE_SELF or 0
@@ -191,18 +227,16 @@
 def clock():
     if _WIN32:
         return win_perf_counter()
-    elif CLOCK_PROCESS_CPUTIME_ID is not None:
+    elif HAS_CLOCK_GETTIME and CLOCK_PROCESS_CPUTIME_ID is not None:
         with lltype.scoped_alloc(TIMESPEC) as a:
-            c_clock_gettime(CLOCK_PROCESS_CPUTIME_ID, a)
-            result = (float(rffi.getintfield(a, 'c_tv_sec')) +
-                      float(rffi.getintfield(a, 'c_tv_nsec')) * 0.000000001)
-        return result
-    else:
-        with lltype.scoped_alloc(RUSAGE) as a:
-            c_getrusage(RUSAGE_SELF, a)
-            result = (decode_timeval(a.c_ru_utime) +
-                      decode_timeval(a.c_ru_stime))
-        return result
+            if c_clock_gettime(CLOCK_PROCESS_CPUTIME_ID, a) == 0:
+                return (float(rffi.getintfield(a, 'c_tv_sec')) +
+                        float(rffi.getintfield(a, 'c_tv_nsec')) * 0.000000001)
+    with lltype.scoped_alloc(RUSAGE) as a:
+        c_getrusage(RUSAGE_SELF, a)
+        result = (decode_timeval(a.c_ru_utime) +
+                  decode_timeval(a.c_ru_stime))
+    return result
 
 # _______________________________________________________________
 # time.sleep()
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
@@ -56,6 +56,11 @@
                                             [rffi.INT], lltype.Void,
                                             compilation_info=eci,
                                             _nowrapper=True)
+    vmprof_get_traceback = rffi.llexternal("vmprof_get_traceback",
+                                  [PVMPROFSTACK, llmemory.Address,
+                                   rffi.SIGNEDP, lltype.Signed],
+                                  lltype.Signed, compilation_info=eci,
+                                  _nowrapper=True)
 
     return CInterface(locals())
 
@@ -154,3 +159,9 @@
 
 def restore_rvmprof_stack(x):
     vmprof_tl_stack.setraw(x)
+
+#
+# traceback support
+
+def get_rvmprof_stack():
+    return vmprof_tl_stack.get_or_make_raw()
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
diff --git a/rpython/rlib/rvmprof/src/rvmprof.h b/rpython/rlib/rvmprof/src/rvmprof.h
--- a/rpython/rlib/rvmprof/src/rvmprof.h
+++ b/rpython/rlib/rvmprof/src/rvmprof.h
@@ -1,3 +1,4 @@
+#include <stdint.h>
 
 RPY_EXTERN char *vmprof_init(int, double, char *);
 RPY_EXTERN void vmprof_ignore_signals(int);
@@ -8,3 +9,6 @@
 RPY_EXTERN int vmprof_stack_append(void*, long);
 RPY_EXTERN long vmprof_stack_pop(void*);
 RPY_EXTERN void vmprof_stack_free(void*);
+RPY_EXTERN intptr_t vmprof_get_traceback(void *, void *, intptr_t*, intptr_t);
+
+#define RVMPROF_TRACEBACK_ESTIMATE_N(num_entries)  (2 * (num_entries) + 4)
diff --git a/rpython/rlib/rvmprof/src/vmprof_common.h b/rpython/rlib/rvmprof/src/vmprof_common.h
--- a/rpython/rlib/rvmprof/src/vmprof_common.h
+++ b/rpython/rlib/rvmprof/src/vmprof_common.h
@@ -128,3 +128,15 @@
     return 0;
 }
 #endif
+
+RPY_EXTERN
+intptr_t vmprof_get_traceback(void *stack, void *ucontext,
+                              intptr_t *result_p, intptr_t result_length)
+{
+    int n;
+    intptr_t pc = ucontext ? GetPC((ucontext_t *)ucontext) : 0;
+    if (stack == NULL)
+        stack = get_vmprof_stack();
+    n = get_stack_trace(stack, result_p, result_length - 2, pc);
+    return (intptr_t)n;
+}
diff --git a/rpython/rlib/rvmprof/test/test_traceback.py b/rpython/rlib/rvmprof/test/test_traceback.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rvmprof/test/test_traceback.py
@@ -0,0 +1,113 @@
+import re
+from rpython.rlib import rvmprof, jit
+from rpython.rlib.rvmprof import traceback
+from rpython.translator.interactive import Translation
+from rpython.rtyper.lltypesystem import lltype
+
+
+def test_direct():


More information about the pypy-commit mailing list