Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions src/ffpuppet/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ def __exit__(self, *exc: object) -> None:

def _benign_sanitizer_report(self, report: Path) -> bool:
"""Scan file for benign sanitizer reports.
# TODO: remove this function.

Args:
report: File to scan.
Expand Down Expand Up @@ -199,23 +200,18 @@ def _benign_sanitizer_report(self, report: Path) -> bool:

return False

def _crashreports(
self, skip_md: bool = False, skip_benign: bool = True
) -> Generator[Path]:
def _crashreports(self, skip_md: bool = False) -> Generator[Path]:
"""Collect crash logs/reports.

Args:
skip_md: Do not scan for minidumps.
skip_benign: Skip reports that only contain benign non-fatal warnings.

Yields:
Log on the filesystem.
"""
assert self._logs.path is not None
# scan for sanitizer logs
for entry in self._logs.path.glob(f"{self._logs.PREFIX_SAN}*"):
if skip_benign and self._benign_sanitizer_report(entry):
continue
yield entry.resolve()
# scan for Valgrind logs
if self._dbg == Debugger.VALGRIND:
Expand Down Expand Up @@ -489,7 +485,7 @@ def close(self, force_close: bool = False) -> None:
assert self._proc_tree is not None
LOG.debug("processes found: %d", self._proc_tree.wait_procs())
# check state of browser processes and set the close reason
if any(self._crashreports(skip_benign=True)):
if any(self._crashreports()):
r_code = Reason.ALERT
# Wait a moment for processes to exit automatically.
# This will allow crash reports to be fully written to disk.
Expand All @@ -504,7 +500,7 @@ def close(self, force_close: bool = False) -> None:
"Slow shutdown detected, %d process(es) still running",
proc_count,
)
crash_reports = set(self._crashreports(skip_benign=True))
crash_reports = set(self._crashreports())
LOG.debug("%d crash report(s) found", len(crash_reports))
if crash_reports:
# additional delay to allow crash reports to be completed/closed
Expand Down Expand Up @@ -538,7 +534,7 @@ def close(self, force_close: bool = False) -> None:
dst_fp=self._logs.add_log(f"ffp_worker_{check.name}")
)
# collect logs (excluding minidumps)
for log_path in self._crashreports(skip_md=True, skip_benign=False):
for log_path in self._crashreports(skip_md=True):
try:
self._logs.add_log(log_path.name, log_path.open("rb"))
except PermissionError: # noqa: PERF203
Expand Down
1 change: 1 addition & 0 deletions src/ffpuppet/puppet_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def __init__(self, base_path: str | None = None) -> None:
self._rr_packed = False
self.closed = True
self.path: Path | None = None
# TODO: remove "watching"
self.watching: dict[str, int] = {}
self.reset()

Expand Down
45 changes: 7 additions & 38 deletions src/ffpuppet/test_ffpuppet.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,14 +468,11 @@ def test_ffpuppet_20(mocker, tmp_path):
with FFPuppet() as ffp:
ffp.launch(TESTFF_BIN)
assert ffp._logs.path is not None
test_logs = [ffp._logs.path / f"{ffp._logs.PREFIX_SAN}.{i}" for i in range(4)]
# ignore benign ASan warning
test_logs[0].write_text("==123==WARNING: Symbolizer buffer too small")
assert ffp.is_healthy()
test_logs = [ffp._logs.path / f"{ffp._logs.PREFIX_SAN}.{i}" for i in range(3)]
# small log with nothing interesting
test_logs[1].write_text("SHORT LOG\nfiller line")
test_logs[0].write_text("SHORT LOG\nfiller line")
# crash on another thread (error log must start on 2nd line)
test_logs[2].write_text(
test_logs[1].write_text(
"GOOD LOG\n"
"==70811==ERROR: AddressSanitizer: SEGV on unknown address 0x00000BADF00D "
"(pc 0x7f4c0bb54c67 bp 0x7f4c07bea380 sp 0x7f4c07bea360 T0)\n"
Expand All @@ -484,7 +481,7 @@ def test_ffpuppet_20(mocker, tmp_path):
"filler line\nfiller line\nfiller line\n"
)
# child log that should be ignored (created when parent crashes)
test_logs[3].write_text(
test_logs[2].write_text(
"BAD LOG\n"
"==70811==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 "
"(pc 0x7f4c0bb54c67 bp 0x7f4c07bea380 sp 0x7f4c07bea360 T2)\n"
Expand All @@ -503,7 +500,7 @@ def test_ffpuppet_20(mocker, tmp_path):
logs = tmp_path / "logs"
ffp.save_logs(logs)
logfiles = tuple(logs.iterdir())
assert len(logfiles) == 6
assert len(logfiles) == 5
for logfile in logfiles:
if "log_ffp_asan_" not in str(logfile):
assert logfile.name in {"log_stderr.txt", "log_stdout.txt"}
Expand All @@ -513,7 +510,6 @@ def test_ffpuppet_20(mocker, tmp_path):
"BAD LOG\n",
"GOOD LOG\n",
"SHORT LOG\n",
"==123==WARNING: Symbolizer buffer too small",
}
assert not any(f.is_file() for f in test_logs)

Expand Down Expand Up @@ -604,16 +600,6 @@ def close(self, force_close=False):
assert ffp._dbg == debugger
ffp.launch()
assert not any(ffp._crashreports())
# benign sanitizer warnings
assert ffp._logs.path is not None
ign_log = ffp._logs.path / f"{ffp._logs.PREFIX_SAN}.1"
ign_log.write_text(
"==123==WARNING: Symbolizer buffer too small\n\n"
"==123==WARNING: Symbolizer buffer too small\n\n"
"==123==WARNING: AddressSanitizer failed to allocate 0xFFFFFF bytes\n"
"==123==AddressSanitizer: soft rss limit exhausted (5000Mb vs 5026Mb)\n"
)
assert any(ffp._crashreports(skip_benign=False))
# valid sanitizer log
san_log = ffp._logs.path / f"{ffp._logs.PREFIX_SAN}.2"
san_log.write_text("test\n")
Expand All @@ -629,17 +615,15 @@ def close(self, force_close=False):
(ffp.profile.path / "minidumps" / "test.dmp").write_text("test\n")
# nothing interesting
(ffp.profile.path / "minidumps" / "test.junk").write_text("\n")
assert not ffp._logs.watching
assert len(list(ffp._crashreports())) == (3 if is_linux else 2)
assert ffp._logs.watching
assert len(list(ffp._crashreports(skip_md=True))) == (2 if is_linux else 1)
if system() != "Windows":
# fail to open (for read) and scan sanitizer file
# not tested on Windows because chmod() does not work
ffp._logs.watching.clear()
ign_log = ffp._logs.path / f"{ffp._logs.PREFIX_SAN}.1"
ign_log.write_text("foo\n")
ign_log.chmod(S_IWRITE)
assert len(list(ffp._crashreports())) == (4 if is_linux else 3)
assert not ffp._logs.watching


def test_ffpuppet_24(mocker, tmp_path):
Expand Down Expand Up @@ -873,21 +857,6 @@ def launch(self):
assert fake_wait_files.call_count == 0


def test_ffpuppet_28():
"""test ignoring benign sanitizer logs"""
with FFPuppet() as ffp:
ffp.launch(TESTFF_BIN)
assert ffp._logs.path is not None
san_log = ffp._logs.path / f"{ffp._logs.PREFIX_SAN}.1"
assert not ffp._logs.watching
# ignore benign ASan warning
san_log.write_text("==123==WARNING: Symbolizer buffer too small")
assert ffp.is_healthy()
assert str(san_log) in ffp._logs.watching
ffp.close()
assert ffp.reason == Reason.CLOSED


@mark.parametrize(
"bin_exists, expect_exc",
[
Expand Down
Loading