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