Commit 7a837040 authored by Kirill Smelkov's avatar Kirill Smelkov

X print wcfs.py py-level traceback on SIGBUS (e.g. wcfs.go aborting due to bug/panic)

Needs https://github.com/cython/cython/pull/2997
parent d8d42734
......@@ -21,9 +21,68 @@
"""Module wcfs_test complements wcfs_test.py with things that are impossible to implement in Python"""
from posix.signal cimport sigaction, sigaction_t, siginfo_t, SA_SIGINFO
from libc.signal cimport SIGBUS
from libc.stdlib cimport abort
from libc.string cimport strlen
from posix.unistd cimport write, sleep
from cpython.exc cimport PyErr_SetFromErrno
from cpython.pystate cimport PyGILState_Ensure, PyGILState_Release, PyGILState_STATE
# XXX -> cpython.lifecycle? .run ?
cdef extern from "Python.h":
int PyRun_SimpleString(const char *)
# read0_nogil accesses mem[0] with GIL released and returns first byte read from there.
def read0_nogil(const unsigned char[::1] mem not None) -> int:
cdef unsigned char b
with nogil:
b = mem[0]
return b
# ---- signal handling ----
# XXX -> golang.signal ?
# install_sigbus_trap installs SIGBUS handler that prints python-level
# traceback before aborting.
#
# Such handler is useful, because when wcfs.go bugs/panics while handling file
# access from wcfs.py, wcfs.py receives SIGBUS signal and by default aborts.
def install_sigbus_trap():
cdef sigaction_t act
act.sa_sigaction = on_sigbus
act.sa_flags = SA_SIGINFO
cdef int err = sigaction(SIGBUS, &act, NULL)
if err:
PyErr_SetFromErrno(OSError)
cdef void on_sigbus(int sig, siginfo_t *si, void *_uc):
# - wait a bit to give time for other threads to complete their exception dumps
# (e.g. getting "Transport endpoint is not connected" after wcfs.go dying)
# - dump py-level traceback and abort.
# TODO turn SIGBUS into python-level exception? (see sigpanic in Go how to do).
writeerr("\nC: SIGBUS received; giving time to other threads" +
"to dump their exceptions (if any) ...\n")
cdef PyGILState_STATE gstate = PyGILState_Ensure()
PyGILState_Release(gstate)
sleep(1)
writeerr("\nC: SIGBUS thread traceback:\n")
PyGILState_Ensure()
PyRun_SimpleString("import traceback; traceback.print_stack()")
writeerr("-> SIGBUS\n");
abort()
# writeerr writes msg to stderr without depending on stdio buffering and locking.
cdef void writeerr(const char *msg):
cdef ssize_t n, left = strlen(msg)
while left > 0:
n = write(2, msg, left)
if n == -1:
return # nothing we can do under crash?
left -= n
msg += n
......@@ -42,7 +42,7 @@ from zodbtools.util import ashex as h, fromhex
from pytest import raises
from six import reraise
from .internal import mm
from .internal.wcfs_test import read0_nogil
from .internal.wcfs_test import read0_nogil, install_sigbus_trap
# setup:
# - create test database, compute zurl and mountpoint for wcfs
......@@ -52,6 +52,10 @@ testdb = None
testzurl = None # URL of testdb
testmntpt = None # wcfs is mounted here
def setup_module():
# if wcfs.py receives SIGBUS because wcfs.go panics, we want to see
# python-level traceback instead of being killed.
install_sigbus_trap()
global testdb, testzurl, testmntpt
testdb = getTestDB()
testdb.setup()
......@@ -1172,7 +1176,7 @@ def test_wcfs():
# >>> XXX commit data to not yet accessed f part - nothing happens
"""
# """
# >>> invalidation protocol
print('\n\n inv. protocol \n\n')
......@@ -1238,7 +1242,7 @@ def test_wcfs():
for at in revv[1:]:
wl.watch(zf, at)
wl.close()
"""
# """
# XXX move before setup watch?
print('\n\n\n\nWATCH+COMMIT\n\n')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment