| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Adds UnixWorkerBackend (Option C): the same DirectBackend, but in a child
process that opens idalib on ITS main thread and serves one client serially over
an AF_UNIX socket with length-prefixed pickle (bytes ride raw — no hex, no JSON).
bench() is now generic over {direct, unix, mcp}; --worker runs the child.
3-way result (echo, us/call):
op direct unix mcp unix-vs-mcp
resolve 1.4 42.9 4809 112x
read_bytes(16) 0.8 78.6 4450 57x
read_bytes(4096) 117.1 144.4 6312 44x
disasm_line 2.9 81.2 5423 67x
xrefs_to 40.6 77.9 4770 61x
decompile(cached) 2907 2712 47456 18x
Takeaways:
* A lean local IPC round-trip is ~40-80us — ~60-110x cheaper than the mcp
HTTP/JSON path (~5ms/call floor), while KEEPING crash isolation and the
main-thread decoupling (the freeze/segfault costs of full in-process).
* Bulk bytes are the tell: read_bytes(4096) is 144us unix vs 117us direct (1.2x
overhead) but 6.3ms over mcp — pickle ships 4096 raw bytes; mcp hex-encodes +
JSON-wraps them. The hex view would feel instant on unix.
* ~50us/call = ~20k calls/sec vs mcp's ~200/sec: most of idatui's prefetch/
paging/caching machinery exists to hide the 5ms; on a unix worker you'd barely
need it.
Conclusion this run supports: the sweet spot is Option C (own thin worker), not
full in-process — you capture ~99% of the practical latency win without the UI
freeze during analysis or the loss of crash isolation.
|
|
|
Standalone, not wired into the app. A tiny Backend seam (functions/resolve/
read_bytes/disasm_line/decompile/xrefs_to) with two impls — DirectBackend
(import idapro, in-process) and McpBackend (the current HTTP/JSON tool calls) —
so the "keep the transport or go direct?" question is measurable and feelable.
--bench : A/B latency table (opens a copy in-process; also hits :8745 if up)
--tui : minimal Textual app on the in-process backend; F5 decompiles INLINE
so you feel the main-thread hitch, 'd' decompiles all (big freeze)
Findings (echo, this box), all reproducible:
* open+auto-analysis in-process: ~0.4s (the whole "loading" cost, on the main
thread).
* per-op latency, direct vs mcp:
resolve 2.0us 4755us 2392x
read_bytes(16) 1.2us 4657us 3845x
read_bytes(4096) 112us 6037us 54x
disasm_line 2.9us 5239us 1805x
xrefs_to 25us 4962us 200x
decompile(cached) 2.8ms 51ms 18x
i.e. the mcp transport has a ~5ms/call floor regardless of op; the fast ops
idatui spams while scrolling are 1000-4000x cheaper in-process (which is why
the prefetch/paging/caching machinery exists).
* hard constraints proven separately: idalib must be imported/opened on the MAIN
python thread (installs a SIGINT handler) and every call must be on it
("Function can be called from the main thread only"); execute_sync from a
worker thread HANGS (no UI pump in headless). So in-process, IDA owns the one
main thread and blocks the event loop for each call — fine at <1ms, a hitch at
decompile (~150ms cold), a freeze during analysis. That main-thread coupling,
not just crash isolation, is what the subprocess boundary buys.
|