<feed xmlns='http://www.w3.org/2005/Atom'>
<title>ida-tui.git/idatui/remote_tools.py, branch main</title>
<subtitle>tui for headless ida</subtitle>
<id>https://git.sl0p.foo/ida-tui.git/atom/idatui/remote_tools.py?h=main</id>
<link rel='self' href='https://git.sl0p.foo/ida-tui.git/atom/idatui/remote_tools.py?h=main'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/'/>
<updated>2026-08-07T12:08:12Z</updated>
<entry>
<title>codemode: three defects the A/B benchmark found in the decompiler path</title>
<updated>2026-08-07T12:08:12Z</updated>
<author>
<name>blasty</name>
<email>blasty@local</email>
</author>
<published>2026-08-07T12:08:12Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/commit/?id=ecc58d7725db6d4929ae3e299dea4f0202a81946'/>
<id>urn:sha1:ecc58d7725db6d4929ae3e299dea4f0202a81946</id>
<content type='text'>
Benchmarking the port against master op-by-op (rather than only asking whether
tests pass) turned up three real bugs, all in the most user-visible path:
opening pseudocode.

1. decompile was doing decomp_map's job. It called the full per-column line
   map purely to fill in each line's /*0xEA*/ anchor. The tool ida-tui was
   written against takes ONE get_line_item at column 0 per line; the port took
   one per COLUMN, i.e. thousands of get_line_item+dstr() calls per function
   instead of one per line. Every pseudocode open cost the same as opening the
   split view. Carried the real implementation over: 1888ms -&gt; 53ms.

2. decomp_map used the pre-optimisation line map. Ours memoises obj_id -&gt; ea
   for the whole function (commit 853d90c: dstr() was 79% of the tool, and
   consecutive columns report the same ctree item), the port's did not.
   1925ms -&gt; 287ms.

3. _idatui_compact imported ida_pro_mcp on every call. Under Code Mode that
   package is not installed in the database process, so the import failed every
   time -- and a FAILED import is never cached, so each one re-searched the
   whole of sys.path: 422 failed imports per pc_nums call, which was most of
   its runtime. 1428ms -&gt; 257ms.

   The same bug was a correctness bug hiding behind the perf bug: the fallback
   path collapsed whitespace INSIDE string literals, where the real function
   preserves it. Pseudocode columns are served in those coordinates, so on any
   line containing a string with two spaces, every literal's mark and every
   reformat would have been placed on the wrong column. It never fired on
   master because ida_pro_mcp is installed there. Now calls the byte-identical
   module-level shim directly, with the deviation from the extracted original
   documented in place.

Narrow verification: decomp/split_view/opfmt/follow/comment/structs scenarios,
72 passed, 0 failed. Full gate running separately.
</content>
</entry>
<entry>
<title>codemode: carry over the listing + operand-format tools, and stop reshipping them</title>
<updated>2026-08-07T11:34:03Z</updated>
<author>
<name>blasty</name>
<email>blasty@local</email>
</author>
<published>2026-08-07T11:34:03Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/commit/?id=ddeb7afea4553e73a7c1decd78fde968e40d186e'/>
<id>urn:sha1:ddeb7afea4553e73a7c1decd78fde968e40d186e</id>
<content type='text'>
This closes the five operations the port was missing and restores the listing's
own tooling instead of a re-implementation of it.

idatui/remote_tools.py is the port's IDAPython island: `heads` (the continuous
listing) and `op_format`/`pc_nums`/`pc_num_format` (`o`/`O`), extracted verbatim
from the BODY that server/patch_server.py used to inject. They are real, diffable
source shipped to the database process as text, not string literals, because this
is the most performance-tuned and behaviour-sensitive code in the project.

Why carry `heads` over rather than keep the port's version: the port's rewrite
emitted no per-operand extents ("ops"), so no keypress could show which literal
it would reformat (opfmt_highlight had no two-operand row to find); it had no
digest/`expect` support, so every page was re-sent after any edit; and its span
walk was the per-character loop ours had already been rewritten out of. It also
dropped struct-member expansion sizing and the func banner/label rows' exact
shapes.

The library is installed ONCE per database process (sys.modules, keyed by a hash
of the source) and then called by name. Code Mode's execute_python builds a fresh
namespace per call, so a library exec'd inline is rebuilt every time and its
module-level caches thrown away -- the per-line render lru_cache in particular,
which the perf work sized to 65536 entries. Installing it once took `heads`
count=200 from 181ms to 92ms; the cache reports 211 hits on a second call where
it previously reported none. (Extraction footgun recorded: ast FunctionDef.lineno
points at `def`, not at the decorators, so a naive slice silently drops
@lru_cache.)

Also ported: flowchart, survey_binary, and the xref contract.

Live pilot suite on targets/echo: 301 passed, 0 failed -- identical to master.

Known, quantified, and NOT fixed here: Code Mode's transport is much slower than
the unix-socket worker for the listing's paging. heads count=200 is 2.6ms on
master vs 92ms here, count=500 is 6.3ms vs 214ms. Roughly half of that is
to_jsonable + HTTP framing per call and is inherent to the architecture; the
empty round trip alone is 2ms. The digest/`expect` path (unchanged pages) is the
main mitigation and is restored.
</content>
</entry>
</feed>
