mirror of
https://github.com/mrexodia/ida-pro-mcp.git
synced 2026-10-17 05:06:57 +08:00
0d9a47b0c1
ida_search.find_text on a Unity-scale (.text >> 100MB) binary doesn't poll user_cancelled() often enough — once the call entered the SDK, neither sync.py's set_cancelled timer nor profilefunc could interrupt it for tens of seconds at a time. A single non-existent-pattern scan held the IDA main thread for the full natural run (5+ minutes), and every queued tool call wedged behind it: the exact #235 sticky-failure pattern. The fix walks the address space in Python instead: - Iterate idautils.Heads() within fixed-size address chunks (CHUNK_BYTES=65536). This bounds each next_head C call to at most one 64KB scan, so undefined gaps no longer eat unbounded wall time inside a single uninterruptible SDK call. - Between chunks, check both ida_kernwin.user_cancelled() AND the sync.py-provided monotonic deadline directly. The deadline path matters because sync.py's Timer can be starved by GIL contention on tight loops, leaving set_cancelled never fired; the deadline check is independent and reliable. - On hit / cancel, return cursor={"next": ..., "cancelled": True} so callers can distinguish partial-from-cancel vs. natural-end. Adds an optional `end` parameter mirroring the existing `start` so callers can scope a search to a function/segment/range explicitly. sync.py: expose the per-tool deadline via a thread-local (get_tool_deadline()) so any tool body can self-monitor against the configured timeout, independent of the Timer. scripts/stress_search_cancellation.py: also fix a real bug in the harness's set_server_timeout — it was writing a float to os.environ (silently TypeErrors because env requires str), so the override never took effect. Now stringifies and verifies the value was actually set. Validated against UnityFramework (127MB image, ~21M heads in 27 exec segments) via the headless --spawn harness: all phases pass, search_text returns within 5.00s + grace and the recovery probe is instant.