diff options
| -rw-r--r-- | flake.nix | 3 | ||||
| -rw-r--r-- | pyproject.toml | 1 | ||||
| -rw-r--r-- | src/focaccia/deterministic.py | 48 | ||||
| -rwxr-xr-x | src/focaccia/tools/capture_transforms.py | 10 | ||||
| -rw-r--r-- | src/focaccia/trace.py | 7 | ||||
| -rw-r--r-- | uv.lock | 26 |
6 files changed, 92 insertions, 3 deletions
diff --git a/flake.nix b/flake.nix index a523c21..5aebf84 100644 --- a/flake.nix +++ b/flake.nix @@ -331,7 +331,6 @@ validate-qemu = { type = "app"; - # program = "${packages.focaccia}/bin/validate-qemu"; program = let wrapper = pkgs.writeShellScriptBin "validate-qemu" '' exec ${packages.focaccia}/bin/validate-qemu --gdb "${gdbInternal}/bin/gdb" "$@" @@ -400,6 +399,7 @@ packages.dev rr musl-pkgs.gcc + pkgs.capnproto musl-pkgs.pkg-config ]; @@ -416,6 +416,7 @@ musl-minimal-pkgs.pkgsStatic.gzip musl-minimal-pkgs.pkgsStatic.file musl-pkgs.gcc + pkgs.capnproto musl-pkgs.pkg-config musl-minimal-redis-nocheck ]; diff --git a/pyproject.toml b/pyproject.toml index c92cf14..a6c4460 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ authors = [ dependencies = [ "cffi", "miasm", + "jinja2", "brotli", "pycapnp", "setuptools", diff --git a/src/focaccia/deterministic.py b/src/focaccia/deterministic.py new file mode 100644 index 0000000..d05ac7f --- /dev/null +++ b/src/focaccia/deterministic.py @@ -0,0 +1,48 @@ +"""Parsing of JSON files containing snapshot data.""" + +import os +from typing import Union + +import brotli + +try: + import capnp + rr_trace = capnp.load(file_name='./rr/src/rr_trace.capnp', + imports=[os.path.dirname(p) for p in capnp.__path__]) +except Exception as e: + print(f'Cannot load RR trace loader: {e}') + exit(2) + +Frame = rr_trace.Frame +TaskEvent = rr_trace.TaskEvent +MMap = rr_trace.MMap +SerializedObject = Union[Frame, TaskEvent, MMap] + +class DeterministicLog: + def __init__(self, log_dir: str): + self.base_directory = log_dir + + def events_file(self) -> str: + return os.path.join(self.base_directory, 'events') + + def tasks_file(self) -> str: + return os.path.join(self.base_directory, 'tasks') + + def mmaps_file(self) -> str: + return os.path.join(self.base_directory, 'mmaps') + + def _read(self, file, obj: SerializedObject) -> list[SerializedObject]: + with open(file, 'rb') as f: + f.read(8) + data = brotli.decompress(f.read()) + return obj.read_multiple_bytes_packed(data) + + def events(self): + return self._read(self.events_file(), Frame) + + def tasks(self): + return self._read(self.tasks_file(), TaskEvent) + + def mmaps(self): + return self._read(self.mmaps_file(), MMap) + diff --git a/src/focaccia/tools/capture_transforms.py b/src/focaccia/tools/capture_transforms.py index 0adba43..f41d6f4 100755 --- a/src/focaccia/tools/capture_transforms.py +++ b/src/focaccia/tools/capture_transforms.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import sys import argparse import logging @@ -24,6 +25,8 @@ def main(): prog.add_argument('-r', '--remote', default=False, help='Remote target to trace (e.g. 127.0.0.1:12345)') + prog.add_argument('-l', '--deterministic-log', + help='Path of the directory storing the deterministic log produced by RR') prog.add_argument('--log-level', help='Set the logging level') prog.add_argument('--force', @@ -46,7 +49,12 @@ def main(): else: logging.basicConfig(level=logging.INFO) - env = TraceEnvironment(args.binary, args.args, utils.get_envp()) + detlog = None + if args.deterministic_log: + from focaccia.deterministic import DeterministicLog + detlog = DeterministicLog(args.deterministic_log) + + env = TraceEnvironment(args.binary, args.args, utils.get_envp(), nondeterminism_log=detlog) tracer = SymbolicTracer(env, remote=args.remote, cross_validate=args.cross_validate, force=args.force) trace = tracer.trace() diff --git a/src/focaccia/trace.py b/src/focaccia/trace.py index 829b03f..f274418 100644 --- a/src/focaccia/trace.py +++ b/src/focaccia/trace.py @@ -11,15 +11,20 @@ class TraceEnvironment: binary: str, argv: list[str], envp: list[str], - binary_hash: str | None = None): + binary_hash: str | None = None, + nondeterminism_log = None): self.argv = argv self.envp = envp self.binary_name = binary + self.detlog = nondeterminism_log if binary_hash is None and self.binary_name is not None: self.binary_hash = file_hash(binary) else: self.binary_hash = binary_hash + def is_deterministic(self) -> bool: + return self.detlog is not None + @classmethod def from_json(cls, json: dict) -> TraceEnvironment: """Parse a JSON object into a TraceEnvironment.""" diff --git a/uv.lock b/uv.lock index a9ea1d1..315d51c 100644 --- a/uv.lock +++ b/uv.lock @@ -87,6 +87,7 @@ dependencies = [ { name = "brotli", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, { name = "cffi", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, { name = "cpuid", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "jinja2", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, { name = "miasm", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, { name = "pycapnp", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, { name = "setuptools", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, @@ -106,6 +107,7 @@ requires-dist = [ { name = "brotli" }, { name = "cffi" }, { name = "cpuid", git = "https://github.com/taugoust/cpuid.py.git?rev=master" }, + { name = "jinja2" }, { name = "miasm", directory = "miasm" }, { name = "pycapnp" }, { name = "pyright", marker = "extra == 'dev'" }, @@ -134,6 +136,30 @@ wheels = [ ] [[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, +] + +[[package]] name = "miasm" version = "0.1.5" source = { directory = "miasm" } |