- 23 May, 2019 2 commits
-
-
Kirill Smelkov authored
-
Kirill Smelkov authored
This continues c7c01ce4 (bigfile/zodb: ZODB.Connection can migrate between threads on close/open and we have to care): Until now we were retrieving zconn.transaction_manager on _ZBigFileH init, and further using that transaction manager for every connection reopen. However that is not correct because on every reopen connection can be given new transaction manager. We were not practically hitting the bug because until recently ZODB was, by default, using the same ThreadTransactionManager manager instance as Connection.transaction_manager for all connections, and not doing all steps needed to keep _ZBigFileH.transaction_manager in sync to Connection was forgiven - a particular transaction manager that was used was TransactionManager instance implicitly associated with current thread by global threading.Local transaction.manager . However starting from ZODB 5.5.0 Connection code was changed to remember as .transaction_manager the particular TransactionManager instance without any threading.Local games: https://github.com/zopefoundation/ZODB/commit/b6ac40f153 https://github.com/zopefoundation/ZODB/issues/208 https://github.com/zopefoundation/ZODB/pull/226 Given that we were not syncing properly that broke wendelin.core tests, for example: bigfile/tests/test_filezodb.py::test_bigfile_filezodb_vs_conn_migration Exception in thread Thread-1: Traceback (most recent call last): File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner self.run() File "/usr/lib/python2.7/threading.py", line 754, in run self.__target(*self.__args, **self.__kwargs) File "/home/kirr/src/wendelin/wendelin.core/bigfile/tests/test_filezodb.py", line 401, in T11 transaction.commit() # should be nothing File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/transaction/_manager.py", line 252, in commit return self.manager.commit() File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/transaction/_manager.py", line 131, in commit return self.get().commit() File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/transaction/_transaction.py", line 298, in commit self._synchronizers.map(lambda s: s.beforeCompletion(self)) File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/transaction/weakset.py", line 61, in map f(elt) File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/transaction/_transaction.py", line 298, in <lambda> self._synchronizers.map(lambda s: s.beforeCompletion(self)) File "/home/kirr/src/wendelin/wendelin.core/bigfile/file_zodb.py", line 671, in beforeCompletion assert txn is zconn.transaction_manager.get() AssertionError What is happening here is that one thread used the connection and ZBigFile/_ZBigFileH associated with it, then the connection was closed and released to DB pool. Then the connection was reopened but by another thread and thus with different TransactionManager instance and oops - _ZBigFileH.transaction_manager is different because it is TransactionManager instance that was used by the first thread. Fix it by resyncing _ZBigFileH.transaction_manager on every connection reopen. No new test as existing tests already cover the problem when run with ZODB >= 5.5.0 .
-
- 03 May, 2019 1 commit
-
-
Kirill Smelkov authored
This continues 6ab95220 (t/qemu-runlinux: Issue terminal resize before running program) and fully initializes terminal before spawning user application. This has practical effect to restore line wrapping for xterm, as kernel, initially assuming it has "linux" type terminal, somehow messes xterm settings: before the patch lines that were wider than terminal width were not wrapped and characters in the last position were printed over each other. After the patch printed lines are automatically wrapped and test output is not lost. Tput hint found here: https://unix.stackexchange.com/questions/105958
-
- 17 Mar, 2019 1 commit
-
-
Kirill Smelkov authored
0: ERROR+ on boot/run 1: INFO+ on run 2: INFO+ on boot/run 3: DEBUG+ on boot/run It is convenient not to see large kernel boot log on every test run. "1" (single -v) is also convenient: one can skip the boot log but still see details of what is going on when test workload is run. -vv and -vvv are there to see full picture.
-
- 27 Feb, 2019 3 commits
-
-
Kirill Smelkov authored
bpf is needed for tools like bpftrace. fusectl is needed to observe things like /sys/fs/fuse/connections/X/waiting.
-
Kirill Smelkov authored
Else program inside sees default terminal settings to be 80x25, while after the patch it sees correct settings. Still something is not completely right with terminal settings, as e.g. vsplit is not working correctly in vim (vertical ruler is not straight).
-
Kirill Smelkov authored
Graphics mode runs in another window with its own terminal emulation, so propagating e.g. TERM=xterm to where it is emulated as TERM=linux is not correct.
-
- 13 Feb, 2019 1 commit
-
-
Kirill Smelkov authored
Continuing 76d8f76d (Script to run compiled linux kernel with root fs mounted from host) update the script to run/debug linux inside QEMU: - teach it to run specified program + args, instead of hardcoded /bin/sh; - before tailing to user program, builtin init mounts /proc, /sys, ... inside - previously it was /proc, /sys from host seen on those mountpoints and it was very misleading - e.g. ps was showing processes from host, not inside, etc. - builtin init also cares to run specified program with the same current directory that was current on host, and environments such as $HOME, $PATH, $TERM, ... are also propagated. - allow to optionally run QEMU with graphics, instead of stdout only; - increase available RAM from 128M to 512M (with 128M running py.test inside is failing with fork: not enough memory). This updated version was useful to debug WCFS(*) & FUSE issues by running kirr@deco:~/src/wendelin/wendelin.core/wcfs$ ../t/qemu-runlinux ~/src/linux/linux/arch/x86_64/boot/bzImage py.test -vsx -k test_wcfs See https://marc.info/?l=linux-fsdevel&m=155000277921155&w=2 for details. (*) WCFS is still being draft and worked on t branch.
-
- 11 Jan, 2019 1 commit
-
-
Kirill Smelkov authored
On a testing instance we started to see segfaults in pyvma_dealloc() with inside calls to vma_unmap but with NULL pyvma->fileh. That was strange, becuse before calling vma_unmap(), the code explicitly checks whether pyvma->fileh is !NULL. That was, as it turned out, due to pyvma_dealloc being called twice at the same time from two python threads. Here is how that was possible: T1 decrefs pyvma and finds its reference count drops to zero. It calls pyvma_dealloc. From there vma_unmap() is called, which calls virt_lock() and that releases GIL first. Another thread T2 was waiting for GIL, it acquires it, does some work at python level and somehow triggers GC. Since PyVMA supports cyclic GC, it was on GC list and thus GC calls dealloc for the same vma again. Here is how it looks in the backtraces: T1: #0 0x00007f6aefc57827 in futex_abstimed_wait_cancelable (private=0, abstime=0x0, expected=0, futex_word=0x1e011d0) at ../sysdeps/unix/sysv/linux/futex-internal.h:205 #1 do_futex_wait (sem=sem@entry=0x1e011d0, abstime=0x0) at sem_waitcommon.c:111 #2 0x00007f6aefc578d4 in __new_sem_wait_slow (sem=0x1e011d0, abstime=0x0) at sem_waitcommon.c:181 #3 0x00007f6aefc5797a in __new_sem_wait (sem=<optimized out>) at sem_wait.c:29 #4 0x00000000004ffbc4 in PyThread_acquire_lock () #5 0x00000000004dbe8a in PyEval_RestoreThread () #6 0x00007f6ac6d3b8fc in py_gil_retake_if_waslocked (arg=0x4f18f00) at bigfile/_bigfile.c:1048 #7 0x00007f6ac6d3dcfc in virt_gil_retake_if_waslocked (gilstate=0x4f18f00) at bigfile/virtmem.c:78 #8 0x00007f6ac6d3dd30 in virt_lock () at bigfile/virtmem.c:92 #9 0x00007f6ac6d3e724 in vma_unmap (vma=0x7f6a7e0c4100) at bigfile/virtmem.c:271 #10 0x00007f6ac6d3a0bc in pyvma_dealloc (pyvma0=0x7f6a7e0c40e0) at bigfile/_bigfile.c:284 ... #13 0x00000000004d76b0 in PyEval_EvalFrameEx () T2: #5 0x00007f6ac6d3a081 in pyvma_dealloc (pyvma0=0x7f6a7e0c40e0) at bigfile/_bigfile.c:276 #6 0x0000000000500450 in ?? () #7 0x00000000004ffd82 in _PyObject_GC_New () #8 0x0000000000485392 in PyList_New () #9 0x00000000004d3bff in PyEval_EvalFrameEx () T2 does the work of vma_unmap and clears C-level vma. Then, when T1 wakes up and returns to vma_unmap, it sees vma->file and all other fields cleared -> oops segfault. Fix it by removing pyvma from GC list before going to do actual destruction. This way if a concurrent GC triggers, it won't see the vma object on its list, and thus won't have a chance to invoke its destructor the second time. The bug was introduced in 450ad804 (bigarray: ArrayRef support for BigArray) when PyVMA was changed to be cyclic-GC aware. However at that time, even Python documentation itself was not saying PyObject_GC_UnTrack is needed, as it was added only in 2.7.15 after finding that many types in CPython itself are vulnerable to similar segfaults: https://github.com/python/cpython/commit/4cde4bdcc86 https://bugs.python.org/issue31095 It is pity, that CPython took the approach to force all type authors to care to invoke PyObject_GC_UnTrack explicitly, instead of doing that automatically in Python runtime before calling tp_dealloc. /cc @Tyagov, @klaus /reviewed-on !11
-
- 29 Oct, 2018 2 commits
-
-
Kirill Smelkov authored
Structured creates view of the array interpreting its minor axis as fully covered by a dtype. It is similar to arr.view(dtype) + corresponding reshape, but does not have limitations of ndarray.view(). For example: In [1]: a = np.arange(3*3, dtype=np.int32).reshape((3,3)) In [2]: a Out[2]: array([[0, 1, 2], [3, 4, 5], [6, 7, 8]], dtype=int32) In [3]: b = a[:2,:2] In [4]: b Out[4]: array([[0, 1], [3, 4]], dtype=int32) In [5]: dtxy = np.dtype([('x', np.int32), ('y', np.int32)]) In [6]: dtxy Out[6]: dtype([('x', '<i4'), ('y', '<i4')]) In [7]: b.view(dtxy) --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-66-af98529aa150> in <module>() ----> 1 b.view(dtxy) ValueError: To change to a dtype of a different size, the array must be C-contiguous In [8]: structured(b, dtxy) Out[8]: array([(0, 1), (3, 4)], dtype=[('x', '<i4'), ('y', '<i4')]) Structured always creates view and never copies data. Here is original context where separately playing with .shape and .dtype was not enough, since it was creating array copy and OOM'ing the machine: klaus/wendelin@cbe4938b
-
Kirill Smelkov authored
We are going to use this code in another place, so move this out to dommon place as a preparatory step first. On a related note: Since ArrayRef is generic and quite independent from BigArray (it only supports it, but equally it supports just other - e.g. plain arrays), the proper place for it might be also to be lib/xnumpy.py . We might get to this topic a bit later.
-
- 12 Oct, 2018 2 commits
-
-
Kirill Smelkov authored
RAMArray is compatible to ZBigArray in API and semantic, but stores its data in RAM only. It is useful in situations where ZBigArray compatible data type is needed, but the amount of data is small and the data itself is needed only temporarily - e.g. in a simulation. Please see details in individual patches. Original merge request by @klaus (!8). /cc @Tyagov /reviewed-on !9
-
Kirill Smelkov authored
RAMArray is compatible to ZBigArray in API and semantic, but stores its data in RAM only. It is useful in situations where ZBigArray compatible data type is needed, but the amount of data is small and the data itself is needed only temporarily - e.g. in a simulation. Implementation is based on mmapping temporary files from /dev/shm/... and passing them as file handles, similarly to how ZBigArray works, to BigArray. We don't use just numpy.ndarray because of append - for ZBigArray append works in O(1), but more importantly it does not copy data. This way mmapings previously created for ZBigArray views, continue to correctly alias array data. If we would be using ndarray directly, since ndarray.resize copies data, that property would not be preserved. Original patch by Klaus Wölfel <klaus@nexedi.com> (!8)
-
- 11 Oct, 2018 1 commit
-
-
Kirill Smelkov authored
bigarray/tests: Factor out a way to spcify on which BigFile/BigFileH an array is tested into fixture parameter Currently we have only one BigFile and its BigFileH handle. However in the next patch, for RAMArray, we'll be adding handles for opened RAM files, and it would be good to test whole BigArray functionality on data served by those handles too. Prepare for this and first factor out into testbig fixture the way to open such handles.
-
- 04 Jul, 2018 1 commit
-
-
Kirill Smelkov authored
So that it can be available to everyone and in particular B & friends to be available from introduced importable golang.testing package. The move itself: pygolang@9bf03d9c While moving the code was restructured / improved a bit and py.bench interface reworked to mimic `go test -bench` in defaults.
-
- 26 Jun, 2018 2 commits
-
-
Kirill Smelkov authored
-
Kirill Smelkov authored
-
- 17 Apr, 2018 3 commits
-
-
Kirill Smelkov authored
-
Kirill Smelkov authored
bigfile/tests/test_filezodb.py ........W: testdb: teardown: <Connection at 7f8fe2b43b90> left not closed by test code; opened by: ... File "/home/kirr/src/wendelin/wendelin.core/bigfile/tests/test_filezodb.py", line 754, in test_bigfile_zblk1_zdata_reuse _test_bigfile_zblk1_zdata_reuse() File "/home/kirr/src/wendelin/wendelin.core/bigfile/tests/test_filezodb.py", line 759, in _test_bigfile_zblk1_zdata_reuse root = dbopen() File "/home/kirr/src/wendelin/wendelin.core/bigfile/tests/test_filezodb.py", line 47, in dbopen return testdb.dbopen() File "/home/kirr/src/wendelin/wendelin.core/lib/testing.py", line 188, in dbopen self.connv.append( (weakref.ref(conn), ''.join(traceback.format_stack())) ) lib/tests/test_zodb.py .W: testdb: teardown: <Connection at 7f8fe26f13d0> left not closed by test code; opened by: ... File "/home/kirr/src/wendelin/wendelin.core/lib/tests/test_zodb.py", line 49, in test_deactivate_btree root = dbopen() File "/home/kirr/src/wendelin/wendelin.core/lib/tests/test_zodb.py", line 30, in dbopen return testdb.dbopen() File "/home/kirr/src/wendelin/wendelin.core/lib/testing.py", line 188, in dbopen self.connv.append( (weakref.ref(conn), ''.join(traceback.format_stack())) )
-
Kirill Smelkov authored
If a test forgets to explicitly close ZODB connection it was using, this connection stays alive in transaction synchronizers (it is a weakset), and continues to be used on e.g. transaction.commit() when all synchronizers are invoked. This could lead to crashes like below when underlying ZODB storage was closed by test module teardown and testing moved on to another test module: $ WENDELIN_CORE_TEST_DB="<neo>" py.test bigfile/tests/test_filezodb.py::test_bigfile_zblk1_zdata_reuse lib/tests/test_zodb.py ======= test session starts ======== platform linux2 -- Python 2.7.14+, pytest-3.5.0, py-1.5.3, pluggy-0.6.0 rootdir: /home/kirr/src/wendelin/wendelin.core, inifile: collected 2 items bigfile/tests/test_filezodb.py . [ 50%] lib/tests/test_zodb.py F [100%] ______ test_deactivate_btree _______ def test_deactivate_btree(): root = dbopen() # init btree with many leaf nodes leafv = [] root['btree'] = B = IOBTree() for i in range(10000): B[i] = xi = XInt(i) leafv.append(xi) > transaction.commit() lib/tests/test_zodb.py:56: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../venv/z5/local/lib/python2.7/site-packages/transaction/_manager.py:131: in commit return self.get().commit() ../venv/z5/local/lib/python2.7/site-packages/transaction/_transaction.py:316: in commit self._synchronizers.map(lambda s: s.afterCompletion(self)) ../venv/z5/local/lib/python2.7/site-packages/transaction/weakset.py:62: in map f(elt) ../venv/z5/local/lib/python2.7/site-packages/transaction/_transaction.py:316: in <lambda> self._synchronizers.map(lambda s: s.afterCompletion(self)) ../venv/z5/local/lib/python2.7/site-packages/ZODB/Connection.py:757: in afterCompletion self.newTransaction(transaction, False) ../venv/z5/local/lib/python2.7/site-packages/ZODB/Connection.py:737: in newTransaction invalidated = self._storage.poll_invalidations() ../venv/z5/local/lib/python2.7/site-packages/ZODB/mvccadapter.py:131: in poll_invalidations self._start = p64(u64(self._storage.lastTransaction()) + 1) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <neo.client.Storage.Storage object at 0x7ffa1be8d410> def lastTransaction(self): # Used in ZODB unit tests > return self.app.last_tid E AttributeError: 'NoneType' object has no attribute 'last_tid' ../../neo/src/lab.nexedi.com/kirr/neo/neo/client/Storage.py:181: AttributeError where NEO's Storage.app is None because the storage was closed. ---- To avoid such kind of failures make sure TestDB.teardown() always closes all ZODB connections that were ever opened via TestDB.dbopen(). Add a warning about such force-closing with information about corresponding connection and code place that created it, so that it is easy to understand which test needs a fix. /suggested-by @jm
-
- 16 Apr, 2018 4 commits
-
-
Kirill Smelkov authored
-
Kirill Smelkov authored
- start testing NEO with ZODB4 and ZODB5. ZODB4-*-NEO works while ZODB5-*-NEO currently fails [1]. - update NumPy to latest releases. - update Python 3 to latest releases.
-
Kirill Smelkov authored
Since 7fc4ec66 (tests: Allow to test with ZEO & NEO ZODB storages) we can run the tests with either FileStorage, ZEO or NEO. But ZEO test adapter started to fail with ZEO5: self = <wendelin.lib.testing.TestDB_ZEO object at 0x7f1feb5091d0> def setup(self): port = self.zeo_forker.get_port() zconf = self.zeo_forker.ZEOConfig(('', port)) self.addr, self.adminaddr, self.pid, self.path = \ > self.zeo_forker.start_zeo_server(zeo_conf=zconf, port=port) E ValueError: need more than 2 values to unpack This is because in ZEO5 forker.start_zeo_server() was reworked to return only addr and stop closure instead of returning all details and relying on caller to implement stop itself. Adapt the test to detect ZEO5 and use new calling convention.
-
Kirill Smelkov authored
With ZConfig==3.2.0 (released 2017-06-22) ZEO started to get file names as unicode, which eventually breaks on assert in ZODB3.FileStorage code: Traceback (most recent call last): File "/home/kirr/src/wendelin/r/work/venv/local/lib/python2.7/site-packages/ZEO/tests/zeoserver.py", line 215, in <module> main() File "/home/kirr/src/wendelin/r/work/venv/local/lib/python2.7/site-packages/ZEO/tests/zeoserver.py", line 188, in main server = ZEO.runzeo.create_server({"1": storage}, zo) File "/home/kirr/src/wendelin/r/work/venv/local/lib/python2.7/site-packages/ZEO/runzeo.py", line 366, in create_server auth_realm = options.auth_realm, File "/home/kirr/src/wendelin/r/work/venv/local/lib/python2.7/site-packages/ZEO/StorageServer.py", line 890, in __init__ self._setup_invq(name, storage) File "/home/kirr/src/wendelin/r/work/venv/local/lib/python2.7/site-packages/ZEO/StorageServer.py", line 928, in _setup_invq self.invq[name] = list(lastInvalidations(self.invq_bound)) File "/home/kirr/src/wendelin/r/work/venv/local/lib/python2.7/site-packages/ZODB/FileStorage/FileStorage.py", line 1238, in lastInvalidations for trans in FileIterator(self._file_name, pos=pos)] File "/home/kirr/src/wendelin/r/work/venv/local/lib/python2.7/site-packages/ZODB/FileStorage/FileStorage.py", line 1640, in __init__ assert isinstance(filename, str), `filename` AssertionError: u'/home/kirr/src/wendelin/r/work/wendelin.core/Data.fs' -> So for ZODB3 pin ZConfig to ZConfig <3.2.0 which is known to work with ZODB3.
-
- 12 Apr, 2018 1 commit
-
-
Kirill Smelkov authored
ArrayRef is a reference to NumPy array. The reference is represented by root array object and instructions how to create original array as some view of the root. Such reference could be useful in situations where one needs to pass arrays between processes and instead of copying array data, leverage the fact that top-level array, for example ZBigArray, is already persisted separately, and only send small amount of information referencing data in question. Use `ArrayRef(array)` to create reference to an ndarray. Use `.deref()` to convert ArrayRef to pointed array object. NOTE don't send ArrayRef unconditionally - for example when array object is small regular ndarray with also regular, but big, root ndarray, sending ArrayRef will send whole data for root object, not for small leaf. Sending ArrayRef only makes sense when root object is known to be already persisted by other means, for example something like below in ZODB context: ```python aref = ArrayRef(a) if isinstance(aref.root, Persistent): send aref else: send a ``` Please see individual patches for more details. /reviewed-on !6
-
- 02 Apr, 2018 2 commits
-
-
Kirill Smelkov authored
Rationale --------- Array reference could be useful in situations where one needs to pass arrays between processes and instead of copying array data, leverage the fact that top-level array, for example ZBigArray, is already persisted separately, and only send small amount of information referencing data in question. Implementation -------------- BigArray is not regular NumPy array and so needs explicit support in ArrayRef code to find root object and indices. This patch adds such support via the following way: - when BigArray.__getitem__ creates VMA, it remembers in the VMA the top-level BigArray object under which this VMA was created. - when ArrayRef is finding root, it can detect such VMAs, because it will be pointed to by the most top regular ndarray's .base, and in turn gets top-level BigArray object from the VMA. - further all indices computations are performed, similarly to complete regular ndarrays case, on ndarrays root and a. But in the end .lo and .hi are adjusted for the corresponding offset of where root is inside whole BigArray. - there is no need to adjust .deref() at all. For remembering information into a VMA and also to be able to get (readonly) its mapping addresses _bigfile.c extension has to be extended a bit. Since we are now storing arbitrary python object attached to PyVMA - it can create cycles - and so PyVMA accordingly adjusted to support cyclic garbage collector. Please see the patch itself for more details and comments.
-
Kirill Smelkov authored
ArrayRef is a tool to find out for a NumPy array its top-level root parent and remember instructions how to recreate original array from the root. For example if root = arange(1E7) z = root[1000:2000] a = z[10:20] `ArrayRef(a)` will find out that the root array for `a` is `root` and that `a` occupies 1010:1020 bytes in it. The vice versa operation is also possible, for example given aref = ArrayRef(a) it is possible to restore original `a` from `aref`: a_ = aref.deref() assert array_equal(a_, a) the restoration works without copying by creating appropriate view of root. ArrayRef should work reliably for arrays of arbitrary dimensions, strides etc - even fancy arrays created via stride tricks such as arrays whose elements overlap each other should be supported. This patch adds ArrayRef with support for regular ndarrays only. The next patch will add ArrayRef support for BigArray and description for ArrayRef rationale.
-
- 21 Feb, 2018 1 commit
-
-
Kirill Smelkov authored
This allows e.g. to open `neo://cluster@master?compress=false` - in other words with using options, which our current simplified opening code does not support. Keep old dbstoropen around as the fallback to work when zodbtools/zodburi are not available, since we still want to try to support ZODB 3.10.
-
- 31 Jan, 2018 1 commit
-
-
Kirill Smelkov authored
It was bigfile/pagefault.c:45:36: warning: ‘struct ucontext’ declared inside parameter list will not be visible outside of this definition or declaration static int faulted_by(const struct ucontext *uc); ^~~~~~~~ bigfile/pagefault.c: In function ‘on_pagefault’: bigfile/pagefault.c:59:24: warning: passing argument 1 of ‘faulted_by’ from incompatible pointer type [-Wincompatible-pointer-types] write = faulted_by(uc); ^~ bigfile/pagefault.c:45:12: note: expected ‘const struct ucontext *’ but argument is of type ‘struct ucontext *’ static int faulted_by(const struct ucontext *uc); ^~~~~~~~~~ bigfile/pagefault.c: At top level: bigfile/pagefault.c:208:36: warning: ‘struct ucontext’ declared inside parameter list will not be visible outside of this definition or declaration static int faulted_by(const struct ucontext *uc) ^~~~~~~~ bigfile/pagefault.c:208:12: error: conflicting types for ‘faulted_by’ static int faulted_by(const struct ucontext *uc) ^~~~~~~~~~ bigfile/pagefault.c:45:12: note: previous declaration of ‘faulted_by’ was here static int faulted_by(const struct ucontext *uc); ^~~~~~~~~~ bigfile/pagefault.c: In function ‘faulted_by’: bigfile/pagefault.c:217:15: error: dereferencing pointer to incomplete type ‘const struct ucontext’ write = uc->uc_mcontext.gregs[REG_ERR] & 0x2; ^~ bigfile/pagefault.c: At top level: bigfile/pagefault.c:45:12: warning: ‘faulted_by’ used but never defined static int faulted_by(const struct ucontext *uc); ^~~~~~~~~~ bigfile/pagefault.c:208:12: warning: ‘faulted_by’ defined but not used [-Wunused-function] static int faulted_by(const struct ucontext *uc) ^~~~~~~~~~ Change to using ucontext_t because apparently there is no `struct ucontext` anymore (and man for sigaction says 3rd parameter to hander is of type `ucontext_t *` - not `struct ucontext *` - cast to `void *`) Explicitly include <ucontext.h> because we are dereferencing ucontext_t, even though today it appears to be included by <signal.h>.
-
- 12 Dec, 2017 1 commit
-
-
Kirill Smelkov authored
Benchmark the time it takes for virtmem to handle pagefault with noop loadblk for loadblk both implemented in C and in Python. On my computer it is: name µs/op PagefaultC 269 ± 0% pagefault_py 291 ± 0% Quite a big time in other words. It turned out to be mostly spent in fallocate'ing pages on tmpfs from /dev/shm. Part of the above 269 µs/op is taken by freeing (reclaiming) pages back when benchmarking work size exceed /dev/shm size, and part to allocating. If I limit the work size (via npage in benchmem.c) to be less than whole /dev/shm it starts to be ~ 170 µs/op and with additional tracing it shows as something like this: .. on_pagefault_start 0.954 µs .. vma_on_pagefault_pre 0.954 µs .. ramh_alloc_page_pre 0.954 µs .. ramh_alloc_page 169.992 µs .. vma_on_pagefault 172.853 µs .. vma_on_pagefault_pre 172.853 µs .. vma_on_pagefault 174.046 µs .. on_pagefault_end 174.046 µs .. whole: 171.900 µs so almost all time is spent in ramh_alloc_page which is doing the fallocate: https://lab.nexedi.com/nexedi/wendelin.core/blob/f11386a4/bigfile/ram_shmfs.c#L125 Simple benchmark[1] confirmed it is indeed the case for fallocate(tmpfs) to be relatively slow[2] (and that for recent kernels it regressed somewhat compared to Linux 3.16). Profile flamegraph for that benchmark[3] shows internal loading of shmem_fallocate which for 1 hardware page is not that too slow (e.g. <1µs) but when a request comes for a region internally performs it page by page and so accumulates that ~ 170µs for 2M. I've tried to briefly rerun the benchmark with huge pages activated on /dev/shm via mount /dev/shm -o huge=always,remount as both regular user and as root but it was executing several times slower. Probably something to investigate more later. [1] https://lab.nexedi.com/kirr/misc/blob/4f84a06e/tmpfs/t_fallocate.c [2] https://lab.nexedi.com/kirr/misc/blob/4f84a06e/tmpfs/1.txt [3] https://lab.nexedi.com/kirr/misc/raw/4f84a06e/tmpfs/fallocate-2M-nohuge.svg
-
- 06 Dec, 2017 1 commit
-
-
Kirill Smelkov authored
Rework py.bench output to match output of Go benchmarking[1] so that go tools like benchstat & friends could be used to analyze and compare the timings. Before patch: ============================= test session starts ============================== platform linux2 -- Python 2.7.14, pytest-3.3.2.dev2+g88f2cc9b.d20171206, py-1.5.2, pluggy-0.6.0 rootdir: /home/kirr/src/wendelin/wendelin.core, inifile: collected 11 items pymod: bigfile/tests/bench_0virtmem.py bench_file_mmapread_hole 0.21 (0.39 0.22 0.21) bench_file_read_hole 0.24 (0.24 0.24 0.24) bench_file_readbig_hole 0.30 (0.30 0.31 0.31) bench_bigf_read_hole 0.44 (0.44 0.45 0.44) bench_file_mmapwrite0 0.13 (0.13 0.13 0.13) bench_file_write55 0.08 (0.08 0.08 0.08) bench_bigf_writeff 0.47 (0.47 0.48 0.47) bench_file_mmapread 0.22 (0.22 0.22 0.22) bench_file_read 0.25 (0.25 0.25 0.26) bench_file_readbig 0.31 (0.31 0.31 0.31) bench_bigf_read 0.44 (0.45 0.44 0.44) ========================== 11 passed in 12.92 seconds ========================== After patch: ============================= test session starts ============================== platform linux2 -- Python 2.7.14, pytest-3.3.2.dev2+g88f2cc9b.d20171206, py-1.5.2, pluggy-0.6.0 rootdir: /home/kirr/src/wendelin/wendelin.core, inifile: collected 11 items pymod: bigfile/tests/bench_0virtmem.py Benchmarkfile_mmapread_hole 1 385839.939 µs/op Benchmarkfile_mmapread_hole 1 219214.916 µs/op Benchmarkfile_mmapread_hole 1 210209.131 µs/op Benchmarkfile_read_hole 1 238974.094 µs/op Benchmarkfile_read_hole 1 237294.197 µs/op Benchmarkfile_read_hole 1 238043.070 µs/op Benchmarkfile_readbig_hole 1 301330.090 µs/op Benchmarkfile_readbig_hole 1 301767.111 µs/op Benchmarkfile_readbig_hole 1 301135.063 µs/op Benchmarkbigf_read_hole 1 434718.132 µs/op Benchmarkbigf_read_hole 1 435019.970 µs/op Benchmarkbigf_read_hole 1 434729.099 µs/op Benchmarkfile_mmapwrite0 1 126471.996 µs/op Benchmarkfile_mmapwrite0 1 125886.917 µs/op Benchmarkfile_mmapwrite0 1 125730.038 µs/op Benchmarkfile_write55 1 86760.044 µs/op Benchmarkfile_write55 1 87507.010 µs/op Benchmarkfile_write55 1 87735.891 µs/op Benchmarkbigf_writeff 1 448369.980 µs/op Benchmarkbigf_writeff 1 448238.850 µs/op Benchmarkbigf_writeff 1 447322.845 µs/op Benchmarkfile_mmapread 1 207049.131 µs/op Benchmarkfile_mmapread 1 207813.978 µs/op Benchmarkfile_mmapread 1 210857.868 µs/op Benchmarkfile_read 1 238364.935 µs/op Benchmarkfile_read 1 236908.913 µs/op Benchmarkfile_read 1 238602.161 µs/op Benchmarkfile_readbig 1 303429.842 µs/op Benchmarkfile_readbig 1 302191.973 µs/op Benchmarkfile_readbig 1 304115.057 µs/op Benchmarkbigf_read 1 435079.098 µs/op Benchmarkbigf_read 1 434193.850 µs/op Benchmarkbigf_read 1 434813.976 µs/op ========================== 11 passed in 12.54 seconds ========================== benchstat of new output: name µs/op file_mmapread_hole 272k ±42% file_read_hole 238k ± 0% file_readbig_hole 301k ± 0% bigf_read_hole 435k ± 0% file_mmapwrite0 126k ± 0% file_write55 87.3k ± 1% bigf_writeff 448k ± 0% file_mmapread 209k ± 1% file_read 238k ± 0% file_readbig 303k ± 0% bigf_read 435k ± 0% -------- Not only the output format is reworked here, but also b fixture is added to BenchPlugin so that functions which accept it as arg, can run the benchmark b.N times, can stop/restart timer etc - similar to https://golang.org/pkg/testing/#B . If a bench_* func does not accept b, B is still created, but b.N is assumed to be always 1. The first benchmark which actually uses b will come in the next patch. [1] https://github.com/golang/proposal/blob/master/design/14313-benchmark-format.md
-
- 05 Dec, 2017 1 commit
-
-
Kirill Smelkov authored
Such directories are left in worktree after py.test run.
-
- 04 Dec, 2017 2 commits
-
-
Kirill Smelkov authored
For several benchmarks in a python module instead of always printing whole benchmark function path (nodeid in pytest speak), first print pymod: modulename header, and then only function names. Before patch: ============================= test session starts ============================== platform linux2 -- Python 2.7.14, pytest-3.3.0, py-1.5.2, pluggy-0.6.0 rootdir: /home/kirr/src/wendelin/wendelin.core, inifile: collected 14 items bigfile/tests/bench_0virtmem.py::bench_file_mmapread_hole 0.22 (0.39 0.22 0.22) bigfile/tests/bench_0virtmem.py::bench_file_read_hole 0.24 (0.24 0.24 0.24) bigfile/tests/bench_0virtmem.py::bench_file_readbig_hole 0.30 (0.30 0.30 0.30) bigfile/tests/bench_0virtmem.py::bench_bigf_read_hole 0.43 (0.43 0.43 0.43) bigfile/tests/bench_0virtmem.py::bench_file_mmapwrite0 0.12 (0.13 0.12 0.13) bigfile/tests/bench_0virtmem.py::bench_file_write55 0.08 (0.08 0.08 0.08) bigfile/tests/bench_0virtmem.py::bench_bigf_writeff 0.45 (0.45 0.45 0.45) bigfile/tests/bench_0virtmem.py::bench_file_mmapread 0.21 (0.21 0.21 0.22) bigfile/tests/bench_0virtmem.py::bench_file_read 0.23 (0.23 0.24 0.24) bigfile/tests/bench_0virtmem.py::bench_file_readbig 0.30 (0.30 0.30 0.30) bigfile/tests/bench_0virtmem.py::bench_bigf_read 0.43 (0.44 0.43 0.44) bigfile/tests/bench_1filezodb.py::bench_bigz_readhole 0.42 (0.42 0.43 0.43) bigfile/tests/bench_1filezodb.py::bench_bigz_writeff 0.84 (0.84 1.25 1.25) bigfile/tests/bench_1filezodb.py::bench_bigz_read 0.60 (0.64 0.60 0.60) ========================== 14 passed in 21.78 seconds ========================== After patch: ============================= test session starts ============================== platform linux2 -- Python 2.7.14, pytest-3.3.0, py-1.5.2, pluggy-0.6.0 rootdir: /home/kirr/src/wendelin/wendelin.core, inifile: collected 14 items pymod: bigfile/tests/bench_0virtmem.py bench_file_mmapread_hole 0.21 (0.39 0.22 0.21) bench_file_read_hole 0.24 (0.24 0.24 0.24) bench_file_readbig_hole 0.30 (0.30 0.30 0.30) bench_bigf_read_hole 0.43 (0.43 0.43 0.43) bench_file_mmapwrite0 0.12 (0.13 0.12 0.13) bench_file_write55 0.08 (0.08 0.08 0.08) bench_bigf_writeff 0.45 (0.52 0.51 0.45) bench_file_mmapread 0.21 (0.21 0.21 0.21) bench_file_read 0.24 (0.24 0.24 0.24) bench_file_readbig 0.30 (0.30 0.30 0.30) bench_bigf_read 0.43 (0.43 0.43 0.43) pymod: bigfile/tests/bench_1filezodb.py bench_bigz_readhole 0.43 (0.43 0.43 0.43) bench_bigz_writeff 0.83 (0.83 1.20 1.21) bench_bigz_read 0.60 (0.65 0.60 0.60) ========================== 14 passed in 21.80 seconds ========================== The `key: value` header is compatible with Go benchmark format[1] and further we'll be trying to teach py.bench to output results in that format so that Go benchmarking tools like benchstat and perf.golang.org could be used for free. [1] https://github.com/golang/proposal/blob/master/design/14313-benchmark-format.md
-
Kirill Smelkov authored
Upstream pytest changed TerminalReporter._locationline() signature from def _locationline(self, collect_fspath, fspath, lineno, domain): to def _locationline(self, nodeid, fspath, lineno, domain): https://github.com/pytest-dev/pytest/commit/d73e6899 . This way without adjusting py.bench was reporting just filenames instead of benchmark names, e.g. this way: (z-dev) kirr@deco:~/src/wendelin/wendelin.core$ ./t/py.bench bigfile/tests/bench_0virtmem.py =============================================================== test session starts =============================================================== platform linux2 -- Python 2.7.14, pytest-3.3.0, py-1.5.2, pluggy-0.6.0 rootdir: /home/kirr/src/wendelin/wendelin.core, inifile: collected 11 items bigfile/tests/bench_0virtmem.py 0.21 (0.38 0.22 0.21) bigfile/tests/bench_0virtmem.py 0.23 (0.23 0.24 0.23) bigfile/tests/bench_0virtmem.py 0.30 (0.30 0.30 0.30) bigfile/tests/bench_0virtmem.py 0.43 (0.43 0.43 0.43) bigfile/tests/bench_0virtmem.py 0.12 (0.12 0.12 0.12) bigfile/tests/bench_0virtmem.py 0.08 (0.08 0.08 0.08) bigfile/tests/bench_0virtmem.py 0.44 (0.44 0.44 0.44) bigfile/tests/bench_0virtmem.py 0.20 (0.20 0.20 0.21) bigfile/tests/bench_0virtmem.py 0.24 (0.24 0.24 0.24) bigfile/tests/bench_0virtmem.py 0.30 (0.30 0.30 0.30) bigfile/tests/bench_0virtmem.py 0.43 (0.43 0.43 0.43) =========================================================== 11 passed in 12.39 seconds ============================================================ Fix it.
-
- 02 Dec, 2017 1 commit
-
-
Kirill Smelkov authored
In pytest3 pytest_runtest_call hook is always run as multicall and there is no way to control to disable it and force e.g. 'firstresult' behaviour. This way call.result in pytest_runtest_makereport is always a list of returned results, not the first result itself. Adjust the code in pytest_runtest_makereport to emulate 'firstresult' behaviour manually. Without the patch with recent pytest py.bench was crashing like this: ---- 8< ---- (z-dev) kirr@link:~/src/wendelin/wendelin.core$ make bench python setup.py ll_build_ext --inplace running ll_build_ext copying build/lib.linux-x86_64-2.7/wendelin/bigfile/_bigfile.so -> bigfile python t/py.bench --ignore=3rdparty --ignore=build --ignore=t ============================================================= test session starts ============================================================== platform linux2 -- Python 2.7.14, pytest-3.3.0, py-1.5.2, pluggy-0.6.0 rootdir: /home/kirr/src/wendelin/wendelin.core, inifile: collected 14 items bigfile/tests/bench_0virtmem.py INTERNALERROR> Traceback (most recent call last): INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/_pytest/main.py", line 103, in wrap_session INTERNALERROR> session.exitstatus = doit(config, session) or 0 INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/_pytest/main.py", line 141, in _main INTERNALERROR> config.hook.pytest_runtestloop(session=session) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/__init__.py", line 617, in __call__ INTERNALERROR> return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/__init__.py", line 222, in _hookexec INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/__init__.py", line 216, in <lambda> INTERNALERROR> firstresult=hook.spec_opts.get('firstresult'), INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/callers.py", line 201, in _multicall INTERNALERROR> return outcome.get_result() INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/callers.py", line 77, in get_result INTERNALERROR> _reraise(*ex) # noqa INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/callers.py", line 180, in _multicall INTERNALERROR> res = hook_impl.function(*args) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/_pytest/main.py", line 164, in pytest_runtestloop INTERNALERROR> item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/__init__.py", line 617, in __call__ INTERNALERROR> return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/__init__.py", line 222, in _hookexec INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/__init__.py", line 216, in <lambda> INTERNALERROR> firstresult=hook.spec_opts.get('firstresult'), INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/callers.py", line 201, in _multicall INTERNALERROR> return outcome.get_result() INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/callers.py", line 77, in get_result INTERNALERROR> _reraise(*ex) # noqa INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/callers.py", line 180, in _multicall INTERNALERROR> res = hook_impl.function(*args) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/_pytest/runner.py", line 63, in pytest_runtest_protocol INTERNALERROR> runtestprotocol(item, nextitem=nextitem) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/_pytest/runner.py", line 77, in runtestprotocol INTERNALERROR> reports.append(call_and_report(item, "call", log)) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/_pytest/runner.py", line 161, in call_and_report INTERNALERROR> hook.pytest_runtest_logreport(report=report) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/__init__.py", line 617, in __call__ INTERNALERROR> return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/__init__.py", line 222, in _hookexec INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs) INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/__init__.py", line 216, in <lambda> INTERNALERROR> firstresult=hook.spec_opts.get('firstresult'), INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/callers.py", line 201, in _multicall INTERNALERROR> return outcome.get_result() INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/callers.py", line 77, in get_result INTERNALERROR> _reraise(*ex) # noqa INTERNALERROR> File "/home/kirr/src/wendelin/venv/z-dev/local/lib/python2.7/site-packages/pluggy/callers.py", line 180, in _multicall INTERNALERROR> res = hook_impl.function(*args) INTERNALERROR> File "t/py.bench", line 165, in pytest_runtest_logreport INTERNALERROR> self._tw.write('%.2f' % min(report.bench_times)) INTERNALERROR> TypeError: float argument required, not list
-
- 01 Dec, 2017 2 commits
-
-
Kirill Smelkov authored
If py.test machinery access any of the keys we change before we tweak them - their values will be put to config._inicache and our tweaking won't essentially have the effect. It used to be working without explicit cache invalidation with older pytests, but as of e.g. py.test 3.2.1 it does not work without explicit cache clearing because e.g. 'python_files' is somehow accessed before and is set to std 'test_*.py' + friends in the cache and this way py.bench then does not collect benchmarks from bench_*.py files. Fix it by making sure inicache is invalidated after our inicfg tweak.
-
Kirill Smelkov authored
-
- 24 Oct, 2017 1 commit
-
-
Kirill Smelkov authored
Relicense to GPLv3+ with wide exception for all Free Software / Open Source projects + Business options. Nexedi stack is licensed under Free Software licenses with various exceptions that cover three business cases: - Free Software - Proprietary Software - Rebranding As long as one intends to develop Free Software based on Nexedi stack, no license cost is involved. Developing proprietary software based on Nexedi stack may require a proprietary exception license. Rebranding Nexedi stack is prohibited unless rebranding license is acquired. Through this licensing approach, Nexedi expects to encourage Free Software development without restrictions and at the same time create a framework for proprietary software to contribute to the long term sustainability of the Nexedi stack. Please see https://www.nexedi.com/licensing for details, rationale and options.
-
- 04 Sep, 2017 1 commit
-
-
Kirill Smelkov authored
Since the beginning (1ee72371 "Demo program that shows how to work with ZBigArrays bigger than RAM in size") the work size was 2*RAM. However sometimes for demonstration purposes it could be handy to set it to be even more (e.g. 16*RAM) or vise versa - to something lower. So add command line option to do so instead of manually patching sources every time.
-
- 21 Aug, 2017 1 commit
-
-
Kirill Smelkov authored
A buffer object (pybuf) is passed by C-level loadblk to python loadblk implementation. Since pybuf points to memory that will go away after loadblk call returns to virtmem, PyBigFile tries hard to make sure nothing stays referencing pybuf so it can be released. It tries to: 1. automatically GC cycles referencing pybuf (9aa6a5d7 "bigfile/py: Teach loadblk() to automatically break reference cycles to pybuf") 2. replace pybuf with stub object if a calling frame referencing it still stays alive (61b18a40 "bigfile/py/loadblk: Replace pybuf with a stub object in calling frame in case it stays alive") 3. and as a last resort unpins pybuf from original buffer memeory to point it to NULL (024c246c "bigfile/py/loadblk: Resort to pybuf unpinning, if nothing helps") Step #1 invokes GC. Step #2 calls gc.get_referrers(pybuf) and looks for frames in there. The gc.get_referrers() call happens at python level with allocating some objects, e.g. tuple to pass arguments, resulting list etc. And we all know that any object allocation might cause automatic garbage collection, and GC'ing can in turn ran arbitrary code due to __del__ in release objects and weakrefs callbacks. At a first glance the scenario that GC will be triggered at step #2 looks unrealistic because the GC was just run at step #1 and it is only a few objects being allocated for the call at step #2. However if arbitrary code runs from under GC it can create new garbage and thus upon returning from gc.collect() the garbage list is not empty as the following program demonstrates: ---- 8< ---- import gc # just an object we can set attributes on class X: pass # call f on __del__ class DelCall: def __init__(self, f): self.f = f def __del__(self): self.f() # _mkgarbage creates n objects of garbage kept referenced from an object cycle # so that only cyclic GC can free them. def _mkgarbage(n): # cycle a, b = X(), X() a.b, b.a = b, a # cycle references [n] garbage a.objv = [X() for _ in range(n)] return a # mkgarbage creates cycled garbage and arranges for twice more garbage to be # created when original garbage is collected def mkgarbage(n): a = _mkgarbage(n) a.ondel = DelCall(lambda : _mkgarbage(2*n)) def main(): for i in xrange(10): mkgarbage(1000) print '> %s' % (gc.get_count(),) n = gc.collect() print '< %s' % (gc.get_count(),) main() ---- 8< ---- kirr@deco:~/tmp/trashme/t$ ./gcmoregarbage.py > (482, 11, 0) < (1581, 0, 0) > (531, 3, 0) < (2070, 0, 0) > (531, 3, 0) < (2070, 0, 0) > (531, 3, 0) < (2070, 0, 0) > (531, 3, 0) < (2070, 0, 0) > (531, 3, 0) < (2070, 0, 0) > (531, 3, 0) < (2070, 0, 0) > (531, 3, 0) < (2070, 0, 0) > (531, 3, 0) < (2070, 0, 0) > (531, 3, 0) < (2070, 0, 0) here lines starting with "<" show amount of live garbage objects after gc.collect() call has been finished. This way on a busy server there could be arrangements when GC is explicitly ran at step #1 and then automatically run at step #2 (because of gc.get_referrers() python-level call) and from under GC #2 arbitrary code runs thus potentially mutating exception state which shows in logs as bigfile/_bigfile.c:685: pybigfile_loadblk: Assertion `!(ts->exc_type || ts->exc_value || ts->exc_traceback)' failed. ---- So don't assume we end with clean exception state after collecting pybuf referrers and just clear exception state once again as we do after explicit GC. Don't make a similar assumption for buffer unpinning as an object is decrefed there and in theory this can run some code. A test is added to automatically exercise exception state clearing for get_referrers code path via approach similar to demonstrated in above - - we generate more garbage from under garbage and also arrange for finalizers, which mutate exceptions state, to be run at GC #2. The test without the fix applied fails like this: bigfile/_bigfile.c:710 pybigfile_loadblk WARN: python thread-state found with handled but not cleared exception state bigfile/_bigfile.c:711 pybigfile_loadblk WARN: I will dump it and then crash ts->exc_type: None ts->exc_value: <nil> ts->exc_traceback: <nil> Segmentation fault (core dumped) The None in ts->exc_type and nil value and traceback are probably coming from here in cpython runtime: https://github.com/python/cpython/blob/883520a8/Python/ceval.c#L3717 Since this took some time to find, more diagnostics is also added before BUG_ONs corresponding to finding unclean exception state.
-