diff --git a/src/hexgraph/sandbox/probes/pyghidra_lib.py b/src/hexgraph/sandbox/probes/pyghidra_lib.py index aa97a43..cc9dc78 100644 --- a/src/hexgraph/sandbox/probes/pyghidra_lib.py +++ b/src/hexgraph/sandbox/probes/pyghidra_lib.py @@ -188,16 +188,25 @@ def open_target(artifact, *, cold_analyze=True, read_only=False): def _slow_analyzer(name: str) -> bool: - """The auto-analysis passes disabled under the fast profile — matched by suffix so it's - architecture-agnostic ("PowerPC/ARM/x86 … Constant Reference Analyzer"). Mirrors the Jython - FAST_PROFILE_SCRIPT: drop the O(n^2) Call-Fixup Installer, the per-processor constant/scalar - propagation, and the decompile-EVERY-function passes; KEEP function/call-graph/reference - discovery (HexGraph decompiles on demand).""" + """The auto-analysis passes disabled under the fast profile — matched by name, with the + constant-prop passes matched by suffix so it's architecture-agnostic ("PowerPC/ARM/x86 … + Constant Reference Analyzer"). Mirrors the Jython FAST_PROFILE_SCRIPT: drop the O(n^2) + Call-Fixup Installer, the per-processor constant/scalar propagation, the decompile-EVERY-function + passes, and the Non-Returning Functions analyzers (their ClearFlowAndRepair wedges on a monolith); + KEEP function/call-graph/reference discovery (HexGraph decompiles on demand).""" if "." in name: return False if name in ("Call-Fixup Installer", "Decompiler Parameter ID", "Decompiler Switch Analysis", "Aggressive Instruction Finder"): return True + # Non-Returning Functions ("- Discovered"/"- Known"): when a heavily-called routine is flagged + # non-returning, the analyzer's ClearFlowAndRepair walks the instruction-flow graph (a CodeManager + # DB lookup per node) to repair downstream flow. On a monolith with dense/mis-disassembled code + # that traversal explodes — observed wedged for >40h in FindNoReturnFunctionsAnalyzer.repairDamagedLocations + # on a ~900 MB image. Flow past no-return calls stays intact; we lose only the auto no-return marking, + # which does not affect on-demand xrefs/decompiles. + if name.startswith("Non-Returning Functions"): + return True return name.endswith("Constant Reference Analyzer") or name.endswith("Scalar Operand References") diff --git a/tests/test_ghidra_fast_profile.py b/tests/test_ghidra_fast_profile.py index cddc71d..e3be90e 100644 --- a/tests/test_ghidra_fast_profile.py +++ b/tests/test_ghidra_fast_profile.py @@ -1,6 +1,7 @@ """Ghidra's analysis of a 100 MB+ monolith is bounded by a fast profile that disables the passes proven pathological on a huge binary (Call-Fixup Installer's O(n^2) AddressSet, the per-processor -Constant Reference Analyzer, the decompile-every-function passes) while KEEPING the call-graph/ +Constant Reference Analyzer, the decompile-every-function passes, and the Non-Returning Functions +analyzers whose ClearFlowAndRepair wedges on a monolith) while KEEPING the call-graph/ reference analyzers. Since the PyGhidra re-platform these are pure host-side helpers in `pyghidra_lib` (`_slow_analyzer` + `_FAST_PROFILE_BYTES`), applied in-process by `_analyze` before AutoAnalysisManager runs. The analysis otherwise runs to completion — `re_analyze` runs it detached with a generous budget @@ -25,6 +26,11 @@ def test_fast_profile_disables_the_proven_slow_passes(): # processor-agnostic match for the constant-propagation passes ("PowerPC/ARM/x86 … "): assert L._slow_analyzer("PowerPC Constant Reference Analyzer") assert L._slow_analyzer("ARM Scalar Operand References") + # Non-Returning Functions analyzers: ClearFlowAndRepair wedges on a monolith (both variants). + assert L._slow_analyzer("Non-Returning Functions - Discovered") + assert L._slow_analyzer("Non-Returning Functions - Known") + # ...but a dotted SUB-option of it is still never disabled (the "." rule wins): + assert not L._slow_analyzer("Non-Returning Functions - Discovered.Create Analysis Bookmarks") def test_fast_profile_keeps_the_call_graph_analyzers():