diff --git a/.github/workflows/test-all.yml b/.github/workflows/test-all.yml index b46f969a8..7961dc794 100644 --- a/.github/workflows/test-all.yml +++ b/.github/workflows/test-all.yml @@ -85,11 +85,14 @@ jobs: docker run \ --interactive \ --rm \ + --volume /ofrak_pyghidra_benchmarks:/ofrak_pyghidra/.benchmarks \ --entrypoint bash \ redballoonsecurity/ofrak/ghidra:latest \ -c "python -m ofrak_ghidra.server start \ && ofrak license --community --i-agree \ - && make test" + && make test \ + && make -C /ofrak_pyghidra benchmark" + cat /ofrak_pyghidra_benchmarks/benchmark.json > $GITHUB_STEP_SUMMARY ofrak-angr: name: Test OFRAK angr and capstone components diff --git a/disassemblers/ofrak_pyghidra/Makefile b/disassemblers/ofrak_pyghidra/Makefile index d4c74385a..3780e6a77 100644 --- a/disassemblers/ofrak_pyghidra/Makefile +++ b/disassemblers/ofrak_pyghidra/Makefile @@ -8,5 +8,9 @@ develop: $(PIP) install -e .[test] test: - $(PYTHON) -m pytest --cov=ofrak_pyghidra --cov-report=term-missing tests + $(PYTHON) -m pytest --benchmark-skip --cov=ofrak_pyghidra --cov-report=term-missing tests fun-coverage --cov-fail-under=100 +benchmark: .benchmarks + $(PYTHON) -m pytest --benchmark-only --benchmark-json ./.benchmarks/benchmark.json tests +.benchmarks: + mkdir -p .benchmarks diff --git a/disassemblers/ofrak_pyghidra/setup.py b/disassemblers/ofrak_pyghidra/setup.py index d1d077a83..adabcbd97 100644 --- a/disassemblers/ofrak_pyghidra/setup.py +++ b/disassemblers/ofrak_pyghidra/setup.py @@ -37,6 +37,8 @@ def run(self): "ofrak>=3.3.0rc0", "ofrak_cached_disassembly>=0.1.0", "pyghidra~=2.1.0", + "pytest-benchmark==3.2.3", + "py", ], python_requires=">=3.9", include_package_data=True, diff --git a/disassemblers/ofrak_pyghidra/tests/test_pyghidra_components.py b/disassemblers/ofrak_pyghidra/tests/test_pyghidra_components.py index c417d0237..ae79d2580 100644 --- a/disassemblers/ofrak_pyghidra/tests/test_pyghidra_components.py +++ b/disassemblers/ofrak_pyghidra/tests/test_pyghidra_components.py @@ -1,5 +1,6 @@ from ofrak.core.instruction import Instruction import os +import asyncio from typing import Dict, Tuple from ofrak.core.complex_block import ComplexBlock from ofrak.ofrak_context import OFRAKContext @@ -17,6 +18,10 @@ from ofrak_pyghidra.components.pyghidra_components import _arch_info_to_processor_id from ofrak_type import ArchInfo, Endianness, InstructionSet import ofrak_pyghidra +from ofrak_pyghidra.components.pyghidra_components import ( + PyGhidraAutoAnalyzer, + PyGhidraAutoAnalyzerConfig, +) from ofrak_pyghidra.standalone.pyghidra_analysis import unpack, decompile_all_functions ASSETS_DIR = os.path.abspath( @@ -151,6 +156,58 @@ async def test_decompilation(ofrak_context: OFRAKContext): assert "print" in " ".join(decomps) +async def test_pyghidra_auto_analysis_with_decomp(ofrak_context: OFRAKContext): + root_resource = await ofrak_context.create_root_resource_from_file( + os.path.join(ASSETS_DIR, "hello.x64.elf") + ) + await root_resource.identify() + await root_resource.run( + PyGhidraAutoAnalyzer, config=PyGhidraAutoAnalyzerConfig(decomp=True, language=None) + ) + await root_resource.unpack_recursively() + complex_blocks: List[ComplexBlock] = await root_resource.get_descendants_as_view( + ComplexBlock, + r_filter=ResourceFilter( + tags=[ + ComplexBlock, + ] + ), + ) + decomps = [] + for complex_block in complex_blocks: + await complex_block.resource.run(DecompilationAnalyzer) + pyghidra_resource: DecompilationAnalysis = await complex_block.resource.view_as( + DecompilationAnalysis + ) + decomps.append(pyghidra_resource.decompilation) + assert len(decomps) == 14 + assert "" not in decomps + assert "main" in " ".join(decomps) + assert "print" in " ".join(decomps) + + +def test_unpack_recursively_benchmark(benchmark, ofrak_context): + async def test_unpack_recursively(ofrak_context): + root_resource = await ofrak_context.create_root_resource_from_file( + os.path.join(ASSETS_DIR, "hello.x64.elf") + ) + await root_resource.unpack_recursively( + do_not_unpack=[ + ComplexBlock, + ] + ) + + benchmark(lambda oc: asyncio.run(test_unpack_recursively(oc)), ofrak_context) + + +def test_decompilation_benchmark(benchmark, ofrak_context): + benchmark(lambda oc: asyncio.run(test_decompilation(oc)), ofrak_context) + + +def test_pyghidra_auto_analysis_with_decomp_benchmark(benchmark, ofrak_context): + benchmark(lambda oc: asyncio.run(test_pyghidra_auto_analysis_with_decomp(oc)), ofrak_context) + + async def test_pyghidra_standalone_unpack_decompiled(): program_file = os.path.join(ASSETS_DIR, "hello.x64.elf") decompiled = True