Commit fdc1e9df authored by Ramon Nou's avatar Ramon Nou
Browse files

diags out

parent a4004a61
Loading
Loading
Loading
Loading
+0 −147
Changes for tests/integration/directories/test_sfind_diagnostic.py: 0 added lines, 147 removed lines.
Original line number Diff line number Diff line
import logging
import os
import time
from pathlib import Path

import pytest

from harness.gkfs import Client, Daemon, ShellClient, find_command


log = logging.getLogger(__name__)

SFIND_DIAG_TIMEOUT = 180


def _read_text(path, limit=12000):
    try:
        data = Path(path).read_text(errors="replace")
    except OSError as exc:
        return f"<could not read {path}: {exc}>"
    if len(data) <= limit:
        return data
    return data[-limit:]


def _collect_workspace_logs(test_workspace):
    chunks = []
    for base in (test_workspace.logdir, test_workspace.twd):
        if not Path(base).exists():
            continue
        for path in sorted(Path(base).glob("**/*")):
            if path.is_file() and path.suffix in (".log", ".txt"):
                chunks.append(f"\n--- {path} ---\n{_read_text(path)}")
    return "".join(chunks) if chunks else "<no workspace logs found>"


def _sfind_env(conf, buff_size, iteration):
    return {
        "GKFS_USE_DIRENTS_COMPRESSION": conf["compress"],
        "LIBGKFS_DENTRY_CACHE": conf["cache"],
        "LIBGKFS_DIRENTS_BUFF_SIZE": str(buff_size),
        "LIBGKFS_LOG": "debug",
        "LIBGKFS_LOG_PER_PROCESS": "ON",
        "MALLOC_CHECK_": "3",
        "MALLOC_PERTURB_": str(1 + (iteration % 254)),
        "SFIND_NUM_THREADS": str(conf["threads"]),
    }


@pytest.mark.parametrize("buff_size", ["4096", "5242880"])
@pytest.mark.parametrize("conf", [
    {"compress": "OFF", "cache": "OFF", "threads": 1},
    {"compress": "ON", "cache": "OFF", "threads": 1},
    {"compress": "OFF", "cache": "ON", "threads": 1},
    {"compress": "ON", "cache": "ON", "threads": 1},
    {"compress": "OFF", "cache": "OFF", "threads": 20},
    {"compress": "ON", "cache": "ON", "threads": 20},
])
def test_sfind_repeated_teardown_diagnostic(test_workspace, request, conf, buff_size):
    """Stress sfind until the random SIGSEGV is reproducible with logs.

    The observed failure exits with -11 after printing MATCHED 2000/2000, so
    this diagnostic repeats only the sfind phase and records whether the crash
    depends on compression, dentry cache, buffer size, or sfind thread count.
    """

    repetitions = int(os.environ.get("GKFS_SFIND_DIAG_REPETITIONS", "25"))
    file_count = int(os.environ.get("GKFS_SFIND_DIAG_FILES", "2000"))

    populate_daemon = Daemon(
        request.config.getoption("--interface"),
        "rocksdb",
        test_workspace,
        env={
            "GKFS_DAEMON_LOG_LEVEL": "debug",
            "GKFS_USE_DIRENTS_COMPRESSION": "OFF",
        })
    populate_daemon.run()

    mount_dir = test_workspace.mountdir
    test_dir = mount_dir / "sfind_diagnostic_dir"
    io_client = Client(test_workspace)
    try:
        ret = io_client.mkdir(str(test_dir), 0o755)
        assert ret.retval == 0, f"mkdir failed: errno={ret.errno}"

        ret = io_client.create_n_files(
            str(test_dir),
            file_count,
            env={
                "GKFS_USE_DIRENTS_COMPRESSION": "OFF",
                "LIBGKFS_DENTRY_CACHE": "OFF",
                "LIBGKFS_LOG": "debug",
                "LIBGKFS_LOG_PER_PROCESS": "ON",
            })
        assert ret.retval == 0, f"population failed: errno={ret.errno}"
        assert ret.files_created == file_count
    finally:
        populate_daemon.shutdown()
        time.sleep(1)

    test_daemon = Daemon(
        request.config.getoption("--interface"),
        "rocksdb",
        test_workspace,
        env={
            "GKFS_DAEMON_LOG_LEVEL": "debug",
            "GKFS_USE_DIRENTS_COMPRESSION": conf["compress"],
        })
    test_daemon.run()

    sfind_bin = find_command("sfind", test_workspace.bindirs)
    assert sfind_bin, "sfind binary not found"

    try:
        shell = ShellClient(test_workspace)
        failures = []
        for iteration in range(repetitions):
            env = _sfind_env(conf, buff_size, iteration)
            ret = shell.run(
                str(sfind_bin), str(test_dir), "-S", "1", "-M", str(mount_dir),
                timeout=SFIND_DIAG_TIMEOUT, env=env)
            stdout = ret.stdout.decode(errors="replace") if ret.stdout else ""
            stderr = ret.stderr.decode(errors="replace") if ret.stderr else ""
            log.info(
                "sfind diagnostic iteration=%s conf=%s buff_size=%s exit=%s\n"
                "stdout:\n%s\nstderr:\n%s",
                iteration, conf, buff_size, ret.exit_code, stdout, stderr)

            if ret.exit_code != 0 or f"MATCHED {file_count}/{file_count}" not in stdout:
                failures.append((iteration, ret.exit_code, stdout, stderr))
                if ret.exit_code == -11:
                    break

        if failures:
            iteration, exit_code, stdout, stderr = failures[0]
            pytest.fail(
                "sfind diagnostic reproduced failure\n"
                f"iteration: {iteration}\n"
                f"conf: {conf}\n"
                f"buff_size: {buff_size}\n"
                f"exit_code: {exit_code}\n"
                f"stdout:\n{stdout}\n"
                f"stderr:\n{stderr}\n"
                f"workspace logs:\n{_collect_workspace_logs(test_workspace)}")
    finally:
        test_daemon.shutdown()
 No newline at end of file