diff --git a/lisa/_assets/binaries/x86_64/trace-dump b/lisa/_assets/binaries/x86_64/trace-dump index 25841c0098..6835bffe2f 100755 Binary files a/lisa/_assets/binaries/x86_64/trace-dump and b/lisa/_assets/binaries/x86_64/trace-dump differ diff --git a/lisa/trace.py b/lisa/trace.py index 168bf551c8..d2ebe28d73 100644 --- a/lisa/trace.py +++ b/lisa/trace.py @@ -427,6 +427,17 @@ def get_metadata(self, key): super().get_metadata(key=key) +class TraceDumpError(Exception): + """ + Exception containing errors forwarded from the trace-dump parser + """ + def __init__(self, errors): + self.errors = sorted(set(errors)) + + def __str__(self): + return '\n'.join(self.errors) + + class TraceDumpTraceParser(TraceParserBase): """ trace.dat parser shipped by LISA @@ -436,7 +447,7 @@ def __init__(self, path, events, needed_metadata=None, **kwargs): super().__init__(events=events, needed_metadata=needed_metadata, **kwargs) self._trace_path = str(Path(path).resolve()) self._metadata = {} - self._parquet_dir = None + self._temp_dir = None def __enter__(self): events = self._requested_events @@ -447,38 +458,43 @@ def __enter__(self): ) meta = {} + temp_dir = tempfile.TemporaryDirectory() + self._temp_dir = temp_dir + # time-range will not be available in the basic metadata, this requires # a full parse if events or ('time-range' in needed_metadata): - parquet_dir = tempfile.TemporaryDirectory() - self._parquet_dir = parquet_dir - meta = self._make_parquets( path=path, events=events, - parquet_dir=parquet_dir.name + temp_dir=temp_dir.name ) elif needed_metadata: - meta = self._make_metadata(path=path) + meta = self._make_metadata( + path=path, + temp_dir=temp_dir.name + ) self._metadata.update(meta) return self def __exit__(self, *args, **kwargs): - if (parquet_dir := self._parquet_dir): - parquet_dir.__exit__(*args, **kwargs) + if (temp_dir := self._temp_dir): + temp_dir.__exit__(*args, **kwargs) @classmethod def from_dat(cls, path, events, **kwargs): return cls(path=path, events=events, **kwargs) @classmethod - def _run(cls, cli_args, run_kwargs=None): + def _run(cls, cli_args, temp_dir, run_kwargs=None): logger = cls.get_logger() trace_dump = get_bin('trace-dump') + errors_path = Path(temp_dir) / 'errors.json' cmd = ( trace_dump, + '--errors-json', errors_path, *cli_args, ) pretty_cmd = ' '.join(map(shlex.quote, map(str, cmd))) @@ -501,6 +517,15 @@ def log(stdout, stderr): stdout = completed.stdout log(stdout, completed.stderr) return stdout + finally: + try: + with open(errors_path) as f: + errors = json.load(f) + except FileNotFoundError: + pass + else: + if errors := errors['errors']: + raise TraceDumpError(errors) @classmethod def _process_metadata(cls, meta): @@ -519,20 +544,21 @@ def _process_metadata(cls, meta): return meta @classmethod - def _make_metadata(cls, path): + def _make_metadata(cls, path, temp_dir): stdout = cls._run( - ( + cli_args=( '--trace', path, 'metadata', ), + temp_dir=temp_dir, ) return cls._process_metadata(json.loads(stdout)) @classmethod - def _make_parquets(cls, events, path, parquet_dir): - parquet_dir = Path(parquet_dir) + def _make_parquets(cls, events, path, temp_dir): + temp_dir = Path(temp_dir) cls._run( - ( + cli_args=( '--trace', path, 'parquet', '--unique-timestamps', @@ -546,11 +572,12 @@ def _make_parquets(cls, events, path, parquet_dir): for arg in ('--events', _event) ) ), - dict(cwd=parquet_dir), + run_kwargs=dict(cwd=temp_dir), + temp_dir=temp_dir, ) - with open(parquet_dir / 'meta.json') as metaf: - meta = json.load(metaf) + with open(temp_dir / 'meta.json') as f: + meta = json.load(f) meta = cls._process_metadata(meta) return meta @@ -580,9 +607,9 @@ def __missing__(self, _): def _parse_event(self, event): desc = self._event_descs[event] pid_comms = self._pid_comms - parquet_dir = Path(self._parquet_dir.name) + temp_dir = Path(self._temp_dir.name) - df = pd.read_parquet(parquet_dir / desc['path']) + df = pd.read_parquet(temp_dir / desc['path']) df = self._fixup_df( df=df, pid_comms=pid_comms,