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 @@ ...@@ -21,9 +21,68 @@
"""Module wcfs_test complements wcfs_test.py with things that are impossible to implement in Python""" """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. # 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: def read0_nogil(const unsigned char[::1] mem not None) -> int:
cdef unsigned char b cdef unsigned char b
with nogil: with nogil:
b = mem[0] b = mem[0]
return b 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 ...@@ -42,7 +42,7 @@ from zodbtools.util import ashex as h, fromhex
from pytest import raises from pytest import raises
from six import reraise from six import reraise
from .internal import mm from .internal import mm
from .internal.wcfs_test import read0_nogil from .internal.wcfs_test import read0_nogil, install_sigbus_trap
# setup: # setup:
# - create test database, compute zurl and mountpoint for wcfs # - create test database, compute zurl and mountpoint for wcfs
...@@ -52,6 +52,10 @@ testdb = None ...@@ -52,6 +52,10 @@ testdb = None
testzurl = None # URL of testdb testzurl = None # URL of testdb
testmntpt = None # wcfs is mounted here testmntpt = None # wcfs is mounted here
def setup_module(): 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 global testdb, testzurl, testmntpt
testdb = getTestDB() testdb = getTestDB()
testdb.setup() testdb.setup()
...@@ -1172,7 +1176,7 @@ def test_wcfs(): ...@@ -1172,7 +1176,7 @@ def test_wcfs():
# >>> XXX commit data to not yet accessed f part - nothing happens # >>> XXX commit data to not yet accessed f part - nothing happens
""" # """
# >>> invalidation protocol # >>> invalidation protocol
print('\n\n inv. protocol \n\n') print('\n\n inv. protocol \n\n')
...@@ -1238,7 +1242,7 @@ def test_wcfs(): ...@@ -1238,7 +1242,7 @@ def test_wcfs():
for at in revv[1:]: for at in revv[1:]:
wl.watch(zf, at) wl.watch(zf, at)
wl.close() wl.close()
""" # """
# XXX move before setup watch? # XXX move before setup watch?
print('\n\n\n\nWATCH+COMMIT\n\n') 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