diff options
-rw-r--r-- | README.md | 5 | ||||
-rw-r--r-- | example/poll.c | 22 | ||||
-rw-r--r-- | lib/fuse_signals.c | 15 | ||||
-rw-r--r-- | meson.build | 2 | ||||
-rwxr-xr-x | test/ci-build.sh | 107 | ||||
-rw-r--r-- | test/pytest.ini | 2 | ||||
-rw-r--r-- | test/test_ctests.py | 9 | ||||
-rw-r--r-- | test/util.py | 6 |
8 files changed, 119 insertions, 49 deletions
@@ -73,7 +73,10 @@ nevertheless want to adjust them, you can do so with the *meson configure* command: $ meson configure # list options - $ meson configure -D disable-mtab=true # set an option + $ meson configure -D disable-mtab=true # set an optionq + + $ # ensure all meson options are applied to the final build system + $ meson setup --reconfigure ../ To build, test, and install libfuse, you then use Ninja: diff --git a/example/poll.c b/example/poll.c index f9430a9..fd53ec0 100644 --- a/example/poll.c +++ b/example/poll.c @@ -33,6 +33,7 @@ #include <time.h> #include <pthread.h> #include <poll.h> +#include <stdbool.h> /* * fsel_open_mask is used to limit the number of opens to 1 per file. @@ -51,6 +52,9 @@ static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */ static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */ static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */ static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */ +static _Atomic bool fsel_stop = false; +static pthread_t fsel_producer_thread; + static int fsel_path_index(const char *path) { @@ -61,6 +65,15 @@ static int fsel_path_index(const char *path) return ch <= '9' ? ch - '0' : ch - 'A' + 10; } +static void fsel_destroy(void *private_data) +{ + (void)private_data; + + fsel_stop = true; + + pthread_join(fsel_producer_thread, NULL); +} + static int fsel_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { @@ -205,6 +218,7 @@ static int fsel_poll(const char *path, struct fuse_file_info *fi, } static const struct fuse_operations fsel_oper = { + .destroy = fsel_destroy, .getattr = fsel_getattr, .readdir = fsel_readdir, .open = fsel_open, @@ -220,7 +234,7 @@ static void *fsel_producer(void *data) (void) data; - while (1) { + while (!fsel_stop) { int i, t; pthread_mutex_lock(&fsel_mutex); @@ -263,7 +277,6 @@ static void *fsel_producer(void *data) int main(int argc, char *argv[]) { - pthread_t producer; pthread_attr_t attr; int ret; @@ -279,7 +292,7 @@ int main(int argc, char *argv[]) return 1; } - errno = pthread_create(&producer, &attr, fsel_producer, NULL); + errno = pthread_create(&fsel_producer_thread, &attr, fsel_producer, NULL); if (errno) { perror("pthread_create"); return 1; @@ -287,8 +300,5 @@ int main(int argc, char *argv[]) ret = fuse_main(argc, argv, &fsel_oper, NULL); - pthread_cancel(producer); - pthread_join(producer, NULL); - return ret; } diff --git a/lib/fuse_signals.c b/lib/fuse_signals.c index 048d047..4c1d172 100644 --- a/lib/fuse_signals.c +++ b/lib/fuse_signals.c @@ -16,15 +16,30 @@ #include <string.h> #include <signal.h> #include <stdlib.h> +#include <execinfo.h> static struct fuse_session *fuse_instance; +static void dump_stack(void) +{ +#ifdef HAVE_BACKTRACE + const size_t backtrace_sz = 1024 * 1024; + void* backtrace_buffer[backtrace_sz]; + + int err_fd = fileno(stderr); + + int trace_len = backtrace(backtrace_buffer, backtrace_sz); + backtrace_symbols_fd(backtrace_buffer, trace_len, err_fd); +#endif +} + static void exit_handler(int sig) { if (fuse_instance) { fuse_session_exit(fuse_instance); if(sig <= 0) { fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n"); + dump_stack(); abort(); } fuse_instance->error = sig; diff --git a/meson.build b/meson.build index 7d62dcf..7d8db7f 100644 --- a/meson.build +++ b/meson.build @@ -61,6 +61,8 @@ private_cfg.set('HAVE_SETXATTR', cc.has_function('setxattr', prefix: '#include <sys/xattr.h>')) private_cfg.set('HAVE_ICONV', cc.has_function('iconv', prefix: '#include <iconv.h>')) +private_cfg.set('HAVE_BACKTRACE', + cc.has_function('backtrace', prefix: '#include <execinfo.h>')) # Test if structs have specific member private_cfg.set('HAVE_STRUCT_STAT_ST_ATIM', diff --git a/test/ci-build.sh b/test/ci-build.sh index a023c15..0307865 100755 --- a/test/ci-build.sh +++ b/test/ci-build.sh @@ -1,8 +1,8 @@ -#!/bin/bash +#!/bin/bash -x set -e -TEST_CMD="python3 -m pytest --maxfail=99 test/" +TEST_CMD="pytest -v --maxfail=1 --log-level=DEBUG --log-cli-level=DEBUG test/" SAN="-Db_sanitize=address,undefined" # not default @@ -30,38 +30,48 @@ export LSAN_OPTIONS="suppressions=$(pwd)/lsan_suppress.txt" export ASAN_OPTIONS="detect_leaks=1" export CC -# Standard build -for CC in gcc gcc-9 gcc-10 clang; do - echo "=== Building with ${CC} ===" - mkdir build-${CC}; pushd build-${CC} - if [ "${CC}" == "clang" ]; then - export CXX="clang++" - export TEST_WITH_VALGRIND=false - else - export TEST_WITH_VALGRIND=true - fi - if [ ${CC} == 'gcc-7' ]; then - build_opts='-D b_lundef=false' - else - build_opts='' - fi - if [ ${CC} == 'gcc-10' ]; then - build_opts='-Dc_args=-flto=auto' - else - build_opts='' - fi - meson setup -Dprefix=${PREFIX_DIR} -D werror=true ${build_opts} "${SOURCE_DIR}" || (cat meson-logs/meson-log.txt; false) - ninja - sudo ninja install - - # libfuse will first try the install path and then system defaults - sudo chmod 4755 ${PREFIX_DIR}/bin/fusermount3 - - ${TEST_CMD} - popd - rm -fr build-${CC} - sudo rm -fr ${PREFIX_DIR} -done +non_sanitized_build() +( + echo "Standard build (without sanitizers)" + for CC in gcc gcc-9 gcc-10 clang; do + echo "=== Building with ${CC} ===" + mkdir build-${CC}; pushd build-${CC} + if [ "${CC}" == "clang" ]; then + export CXX="clang++" + export TEST_WITH_VALGRIND=false + else + unset CXX + export TEST_WITH_VALGRIND=true + fi + if [ ${CC} == 'gcc-7' ]; then + build_opts='-D b_lundef=false' + else + build_opts='' + fi + if [ ${CC} == 'gcc-10' ]; then + build_opts='-Dc_args=-flto=auto' + else + build_opts='' + fi + + meson setup -Dprefix=${PREFIX_DIR} -D werror=true ${build_opts} "${SOURCE_DIR}" || (cat meson-logs/meson-log.txt; false) + ninja + sudo ninja install + + # libfuse will first try the install path and then system defaults + sudo chmod 4755 ${PREFIX_DIR}/bin/fusermount3 + + # also needed for some of the tests + sudo chown root:root util/fusermount3 + sudo chmod 4755 util/fusermount3 + + ${TEST_CMD} + popd + rm -fr build-${CC} + sudo rm -fr ${PREFIX_DIR} + + done +) sanitized_build() ( @@ -78,11 +88,24 @@ sanitized_build() # bug, cf. https://groups.google.com/forum/#!topic/mesonbuild/tgEdAXIIdC4 meson configure -D b_lundef=false - meson configure + # additional options + if [ -n "$@" ]; then + meson configure "$@" + fi + + # print all options + meson configure --no-pager + + # reconfigure to ensure it uses all additional options + meson setup --reconfigure "${SOURCE_DIR}" ninja sudo ninja install sudo chmod 4755 ${PREFIX_DIR}/bin/fusermount3 + # also needed for some of the tests + sudo chown root:root util/fusermount3 + sudo chmod 4755 util/fusermount3 + # Test as root and regular user sudo ${TEST_CMD} # Cleanup temporary files (since they are now owned by root) @@ -96,15 +119,17 @@ sanitized_build() ) # Sanitized build -CC=clang -CXX=clang++ +export CC=clang +export CXX=clang++ TEST_WITH_VALGRIND=false -sanitized_build $SAN +sanitized_build # Sanitized build without libc versioned symbols -CC=clang -CXX=clang++ -sanitized_build +export CC=clang +export CXX=clang++ +sanitized_build "-Ddisable-libc-symbol-version=true" + +non_sanitized_build # Documentation. (cd "${SOURCE_DIR}"; doxygen doc/Doxyfile) diff --git a/test/pytest.ini b/test/pytest.ini index 036a9f4..bbc8de8 100644 --- a/test/pytest.ini +++ b/test/pytest.ini @@ -2,3 +2,5 @@ addopts = --verbose --assert=rewrite --tb=native -x -r a markers = uses_fuse: Indicates that FUSE is supported. +log_cli=true +faulthandler_timeout=60 diff --git a/test/test_ctests.py b/test/test_ctests.py index 951a34b..55db156 100644 --- a/test/test_ctests.py +++ b/test/test_ctests.py @@ -9,10 +9,11 @@ import subprocess import pytest import platform import sys +import os from looseversion import LooseVersion from util import (wait_for_mount, umount, cleanup, base_cmdline, safe_sleep, basename, fuse_test_marker, fuse_caps, - fuse_proto) + fuse_proto, create_tmpdir) from os.path import join as pjoin import os.path @@ -29,6 +30,9 @@ def test_write_cache(tmpdir, writeback, output_checker): # deadlock in valgrind, it probably assumes that until close() returns, # control does not come to the program. mnt_dir = str(tmpdir) + print("mnt_dir: '" + mnt_dir + "'") + create_tmpdir(mnt_dir) + cmdline = [ pjoin(basename, 'test', 'test_write_cache'), mnt_dir ] if writeback: @@ -50,6 +54,7 @@ if fuse_proto >= (7,15): @pytest.mark.parametrize("notify", (True, False)) def test_notify1(tmpdir, name, notify, output_checker): mnt_dir = str(tmpdir) + create_tmpdir(mnt_dir) cmdline = base_cmdline + \ [ pjoin(basename, 'example', name), '-f', '--update-interval=1', mnt_dir ] @@ -70,6 +75,7 @@ def test_notify1(tmpdir, name, notify, output_checker): else: assert read1 == read2 except: + print("Failure in notify test: '" + str(cmdline) + "'") cleanup(mount_process, mnt_dir) raise else: @@ -80,6 +86,7 @@ def test_notify1(tmpdir, name, notify, output_checker): @pytest.mark.parametrize("notify", (True, False)) def test_notify_file_size(tmpdir, notify, output_checker): mnt_dir = str(tmpdir) + create_tmpdir(mnt_dir) cmdline = base_cmdline + \ [ pjoin(basename, 'example', 'invalidate_path'), '-f', '--update-interval=1', mnt_dir ] diff --git a/test/util.py b/test/util.py index 0a1fa93..623b031 100644 --- a/test/util.py +++ b/test/util.py @@ -151,6 +151,12 @@ def powerset(iterable): return itertools.chain.from_iterable( itertools.combinations(s, r) for r in range(len(s)+1)) +def create_tmpdir(mnt_dir): + if not os.path.exists(mnt_dir): + print("makedirs: '" + mnt_dir + "'") + os.makedirs(mnt_dir) + else: + print("mnt_dir exists: '" + mnt_dir + "'") # Use valgrind if requested if os.environ.get('TEST_WITH_VALGRIND', 'no').lower().strip() \ |