mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 14:47:40 +08:00
env: suppress liburing TSAN false positives (#14710)
Summary: - Use the liburing TSAN suppressions in `tools/tsan_suppressions.txt` instead of defining the process-wide `__tsan_default_suppressions()` hook, avoiding conflicts with downstream applications. - Wire RocksDB TSAN make and crash-test flows to use that suppressions file by default without overriding caller-provided `TSAN_OPTIONS`. - Cover direct `db_crashtest.py` launches by passing the default suppressions to `db_stress` subprocesses. - Fix the GCC 16 unity-build warning in `CacheItemHelper` by directly initializing the no-secondary-cache helper fields instead of delegating with `this`. Imported from D101303486. Pull Request resolved: https://github.com/facebook/rocksdb/pull/14710 Test Plan: - `COMPILE_WITH_TSAN=1 make -j128 env_test` - `timeout 60s ./env_test --gtest_filter=EnvPosixTest.IOUringAddressReuseNoTsanFalsePositive` - `python3 tools/db_crashtest_test.py` - `python3 -m py_compile tools/db_crashtest.py tools/db_crashtest_test.py` - CI: `build-linux-unity-and-headers` passed after the `CacheItemHelper` fix Reviewed By: hx235 Differential Revision: D104103800 Pulled By: xingbowang fbshipit-source-id: 6066d9abe02a3c44d75f9ce449889468c927ce56
This commit is contained in:
committed by
meta-codesync[bot]
parent
c734b7cc60
commit
224e849e8d
@@ -384,6 +384,10 @@ endif
|
||||
# TSAN doesn't work well with jemalloc. If we're compiling with TSAN, we should use regular malloc.
|
||||
ifdef COMPILE_WITH_TSAN
|
||||
DISABLE_JEMALLOC=1
|
||||
# Use a suppressions file instead of the process-wide TSAN default
|
||||
# suppressions hook, which belongs to the final application.
|
||||
TSAN_OPTIONS?=suppressions=$(CURDIR)/tools/tsan_suppressions.txt
|
||||
export TSAN_OPTIONS
|
||||
EXEC_LDFLAGS += -fsanitize=thread
|
||||
PLATFORM_CCFLAGS += -fsanitize=thread -fPIC -DFOLLY_SANITIZE_THREAD
|
||||
PLATFORM_CXXFLAGS += -fsanitize=thread -fPIC -DFOLLY_SANITIZE_THREAD
|
||||
|
||||
@@ -7,6 +7,13 @@ DB_STRESS_CMD?=./db_stress
|
||||
|
||||
include common.mk
|
||||
|
||||
ifdef COMPILE_WITH_TSAN
|
||||
# Keep direct `make -f crash_test.mk COMPILE_WITH_TSAN=1 ...` runs
|
||||
# aligned with the main Makefile's TSAN runtime options.
|
||||
TSAN_OPTIONS?=suppressions=$(CURDIR)/tools/tsan_suppressions.txt
|
||||
export TSAN_OPTIONS
|
||||
endif
|
||||
|
||||
CRASHTEST_MAKE=$(MAKE) -f crash_test.mk
|
||||
CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD) --cleanup_cmd='$(DB_CLEANUP_CMD)' --destroy_db_initially=1
|
||||
|
||||
|
||||
+18
-2
@@ -30,6 +30,10 @@ _NO_SPACE_SUBSTRINGS = (
|
||||
"enospc",
|
||||
)
|
||||
_OUTPUT_PATH_RE = re.compile(r"(/[^\s]+)")
|
||||
_TSAN_OPTIONS_ENV_VAR = "TSAN_OPTIONS"
|
||||
_TSAN_SUPPRESSIONS_FILE = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "tsan_suppressions.txt")
|
||||
)
|
||||
|
||||
|
||||
def get_random_seed(override):
|
||||
@@ -51,6 +55,16 @@ def quote_arg_for_display(arg):
|
||||
return f"{flag}={shlex.quote(value)}"
|
||||
|
||||
|
||||
def stress_cmd_env():
|
||||
env = os.environ.copy()
|
||||
if (
|
||||
_TSAN_OPTIONS_ENV_VAR not in env
|
||||
and os.path.exists(_TSAN_SUPPRESSIONS_FILE)
|
||||
):
|
||||
env[_TSAN_OPTIONS_ENV_VAR] = "suppressions=" + _TSAN_SUPPRESSIONS_FILE
|
||||
return env
|
||||
|
||||
|
||||
def early_argument_parsing_before_main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
@@ -1905,7 +1919,9 @@ def diagnostic_paths(finalized_params):
|
||||
|
||||
|
||||
def execute_cmd(cmd, timeout=None, timeout_pstack=False):
|
||||
child = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
child = subprocess.Popen(
|
||||
cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, env=stress_cmd_env()
|
||||
)
|
||||
print(
|
||||
"Running db_stress with pid=%d: %s\n\n"
|
||||
% (child.pid, " ".join(quote_arg_for_display(arg) for arg in cmd))
|
||||
@@ -2006,7 +2022,7 @@ def cleanup_after_success(dbname):
|
||||
if parts[0] in ["--env_uri", "--fs_uri"]:
|
||||
cleanup_cmd_parts.append(arg)
|
||||
print("Running DB cleanup command - %s\n" % " ".join(cleanup_cmd_parts))
|
||||
ret = subprocess.call(cleanup_cmd_parts)
|
||||
ret = subprocess.call(cleanup_cmd_parts, env=stress_cmd_env())
|
||||
if ret != 0:
|
||||
print("ERROR: DB cleanup returned error %d\n" % ret)
|
||||
sys.exit(2)
|
||||
|
||||
@@ -16,6 +16,7 @@ import unittest
|
||||
_DB_CRASHTEST_PATH = os.path.join(os.path.dirname(__file__), "db_crashtest.py")
|
||||
_TEST_DIR_ENV_VAR = "TEST_TMPDIR"
|
||||
_TEST_EXPECTED_DIR_ENV_VAR = "TEST_TMPDIR_EXPECTED"
|
||||
_TSAN_OPTIONS_ENV_VAR = "TSAN_OPTIONS"
|
||||
|
||||
|
||||
def load_db_crashtest_module():
|
||||
@@ -40,6 +41,7 @@ class DBCrashTestTest(unittest.TestCase):
|
||||
)
|
||||
self.old_test_tmpdir = os.environ.get(_TEST_DIR_ENV_VAR)
|
||||
self.old_test_expected_tmpdir = os.environ.get(_TEST_EXPECTED_DIR_ENV_VAR)
|
||||
self.old_tsan_options = os.environ.get(_TSAN_OPTIONS_ENV_VAR)
|
||||
os.environ[_TEST_DIR_ENV_VAR] = self.test_tmpdir
|
||||
os.environ.pop(_TEST_EXPECTED_DIR_ENV_VAR, None)
|
||||
|
||||
@@ -54,6 +56,11 @@ class DBCrashTestTest(unittest.TestCase):
|
||||
else:
|
||||
os.environ[_TEST_EXPECTED_DIR_ENV_VAR] = self.old_test_expected_tmpdir
|
||||
|
||||
if self.old_tsan_options is None:
|
||||
os.environ.pop(_TSAN_OPTIONS_ENV_VAR, None)
|
||||
else:
|
||||
os.environ[_TSAN_OPTIONS_ENV_VAR] = self.old_tsan_options
|
||||
|
||||
shutil.rmtree(self.test_tmpdir)
|
||||
|
||||
def load_db_crashtest(self):
|
||||
@@ -66,6 +73,28 @@ class DBCrashTestTest(unittest.TestCase):
|
||||
params.update(overrides)
|
||||
return params
|
||||
|
||||
def test_stress_cmd_env_defaults_tsan_suppressions(self):
|
||||
os.environ.pop(_TSAN_OPTIONS_ENV_VAR, None)
|
||||
db_crashtest = self.load_db_crashtest()
|
||||
|
||||
env = db_crashtest.stress_cmd_env()
|
||||
|
||||
self.assertEqual(
|
||||
"suppressions="
|
||||
+ os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "tsan_suppressions.txt")
|
||||
),
|
||||
env[_TSAN_OPTIONS_ENV_VAR],
|
||||
)
|
||||
|
||||
def test_stress_cmd_env_preserves_tsan_options(self):
|
||||
os.environ[_TSAN_OPTIONS_ENV_VAR] = "halt_on_error=1"
|
||||
db_crashtest = self.load_db_crashtest()
|
||||
|
||||
env = db_crashtest.stress_cmd_env()
|
||||
|
||||
self.assertEqual("halt_on_error=1", env[_TSAN_OPTIONS_ENV_VAR])
|
||||
|
||||
def test_setup_expected_values_dir_preserves_existing_contents(self):
|
||||
os.makedirs(self.expected_dir)
|
||||
marker = os.path.join(self.expected_dir, "marker")
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# ThreadSanitizer suppressions for known third-party false positives.
|
||||
#
|
||||
# Use with:
|
||||
# TSAN_OPTIONS="suppressions=/path/to/rocksdb/tools/tsan_suppressions.txt"
|
||||
#
|
||||
# If the final application already has a TSAN suppressions file, merge these
|
||||
# entries into that application-owned file.
|
||||
#
|
||||
# liburing's io_uring_mmap() uses __sys_mmap, which bypasses TSAN's mmap
|
||||
# interceptor, then immediately reads from the mapped memory in
|
||||
# io_uring_setup_ring_pointers(). When the kernel reuses a virtual address that
|
||||
# was previously mmap'd through libc and tracked by TSAN, TSAN can report a race
|
||||
# between the old mapping's write and the new io_uring read. RocksDB annotates
|
||||
# the io_uring mappings after io_uring_queue_init() returns, but that is too
|
||||
# late for reads that happen inside liburing during initialization.
|
||||
#
|
||||
# Revisit these suppressions when RocksDB upgrades to a liburing version that no
|
||||
# longer uses the raw mmap syscall for io_uring_mmap().
|
||||
race:io_uring_mmap
|
||||
race:io_uring_setup_ring_pointers
|
||||
Reference in New Issue
Block a user