diff --git a/decompiler/backend/codegenerator.py b/decompiler/backend/codegenerator.py index 07583289..d8731e0d 100644 --- a/decompiler/backend/codegenerator.py +++ b/decompiler/backend/codegenerator.py @@ -8,6 +8,8 @@ from decompiler.backend.variabledeclarations import GlobalDeclarationGenerator, LocalDeclarationGenerator from decompiler.task import DecompilerTask +FAIL_MESSAGE = "Decompilation Failed!\n" + class CodeGenerator: """Class in charge of emitting C-code from pseudo code.""" @@ -53,7 +55,8 @@ def generate_function(self, task: DecompilerTask) -> str: @staticmethod def generate_failure_message(task: DecompilerTask): """Returns the message to be shown for a failed task.""" - msg = f"Failed to decompile {task.name}" + msg = FAIL_MESSAGE + msg += f"Failed to decompile {task.name}" if origin := task.failure_origin: # checks if the string is empty (should never be None when this method is called) msg += f" due to error during {origin}." return msg diff --git a/decompiler/frontend/binaryninja/frontend.py b/decompiler/frontend/binaryninja/frontend.py index 432036ef..b1ff994d 100644 --- a/decompiler/frontend/binaryninja/frontend.py +++ b/decompiler/frontend/binaryninja/frontend.py @@ -82,8 +82,7 @@ def lift(self, task: DecompilerTask): task.function_parameter_locations = self._parameter_locations(function) task.complex_types = parser.complex_types except Exception as e: - task.fail("Function lifting") - logging.exception(f"Failed to decompile {task.name}, error during function lifting") + task.fail("Function lifting", e) if task.options.getboolean("pipeline.debug", fallback=False): raise e diff --git a/decompiler/pipeline/pipeline.py b/decompiler/pipeline/pipeline.py index c5d9604e..bebb8459 100644 --- a/decompiler/pipeline/pipeline.py +++ b/decompiler/pipeline/pipeline.py @@ -2,7 +2,7 @@ from __future__ import annotations -from logging import debug, error, warning +from logging import debug, warning from typing import List from decompiler.pipeline.controlflowanalysis.restructuring import PatternIndependentRestructuring @@ -112,8 +112,7 @@ def run(self, task: DecompilerTask): if show_all or stage.name in showed_stages: self._show_stage(task, f"After {stage.name}", print_ascii, show_in_tabs) except Exception as e: - task.fail(origin=stage.name) - error(f"Failed to decompile {task.name}, error during stage {stage.name}: {e}") + task.fail(origin=stage.name, exception=e) if debug_mode: raise e break diff --git a/decompiler/task.py b/decompiler/task.py index f4ff8d33..88f565db 100644 --- a/decompiler/task.py +++ b/decompiler/task.py @@ -1,7 +1,8 @@ """Module describing tasks to be handled by the decompiler pipleline.""" from dataclasses import dataclass, field -from typing import List +from logging import error +from typing import List, Optional from decompiler.structures.ast.syntaxtree import AbstractSyntaxTree from decompiler.structures.graphs.cfg import ControlFlowGraph @@ -40,12 +41,13 @@ def graph(self): def syntax_tree(self): return self.ast - def fail(self, origin: str = ""): + def fail(self, origin: str = "", exception: Optional[Exception] = None): """Sets the task to be failed by setting the failure origin.""" if self.failure_origin is not None: raise RuntimeError("Tried failing already failed task") self._failure_origin = origin + error(f"Failed to decompile {self.name}, error during stage {origin}: {exception}") @property def failed(self) -> bool: diff --git a/tests/test_sample_binaries.py b/tests/test_sample_binaries.py index 8e64e661..f98963af 100644 --- a/tests/test_sample_binaries.py +++ b/tests/test_sample_binaries.py @@ -2,13 +2,14 @@ import subprocess import pytest +from decompiler.backend.codegenerator import FAIL_MESSAGE def test_sample(test_cases): """Test the decompiler with the given test case.""" sample, function_name = test_cases output = subprocess.run(("python", "decompile.py", sample, function_name), check=True, capture_output=True).stdout.decode("utf-8") - assert "Failed to decompile due to error during " not in output + assert FAIL_MESSAGE not in output def test_globals():