RobustLogReader that can recover corrupted bz2 files (#22835)
* LogReader with bzip2 recovery * only rlogs * add comment * plotjuggler should also use robust logreaderpull/22784/head
parent
71e22a6009
commit
6d6f989b7f
3 changed files with 64 additions and 4 deletions
@ -0,0 +1,60 @@ |
||||
#!/usr/bin/env python3 |
||||
import os |
||||
import bz2 |
||||
import urllib.parse |
||||
import subprocess |
||||
import tqdm |
||||
import glob |
||||
from tempfile import TemporaryDirectory |
||||
import capnp |
||||
|
||||
from tools.lib.logreader import FileReader, LogReader |
||||
from cereal import log as capnp_log |
||||
|
||||
|
||||
class RobustLogReader(LogReader): |
||||
def __init__(self, fn, canonicalize=True, only_union_types=False): # pylint: disable=super-init-not-called |
||||
data_version = None |
||||
_, ext = os.path.splitext(urllib.parse.urlparse(fn).path) |
||||
with FileReader(fn) as f: |
||||
dat = f.read() |
||||
|
||||
if ext == "": |
||||
pass |
||||
elif ext == ".bz2": |
||||
try: |
||||
dat = bz2.decompress(dat) |
||||
except ValueError: |
||||
print("Failed to decompress, falling back to bzip2recover") |
||||
with TemporaryDirectory() as directory: |
||||
# Run bzip2recovery on log |
||||
with open(os.path.join(directory, 'out.bz2'), 'wb') as f: |
||||
f.write(dat) |
||||
subprocess.check_call(["bzip2recover", "out.bz2"], cwd=directory) |
||||
|
||||
# Decompress and concatenate parts |
||||
dat = b"" |
||||
for n in sorted(glob.glob(f"{directory}/rec*.bz2")): |
||||
print(f"Decompressing {n}") |
||||
with open(n, 'rb') as f: |
||||
dat += bz2.decompress(f.read()) |
||||
else: |
||||
raise Exception(f"unknown extension {ext}") |
||||
|
||||
progress = None |
||||
while True: |
||||
try: |
||||
ents = capnp_log.Event.read_multiple_bytes(dat) |
||||
self._ents = list(ents) |
||||
break |
||||
except capnp.lib.capnp.KjException: |
||||
if progress is None: |
||||
progress = tqdm.tqdm(total=len(dat)) |
||||
|
||||
# Cut off bytes at the end until capnp is able to read |
||||
dat = dat[:-1] |
||||
progress.update(1) |
||||
|
||||
self._ts = [x.logMonoTime for x in self._ents] |
||||
self.data_version = data_version |
||||
self._only_union_types = only_union_types |
Loading…
Reference in new issue