aboutsummaryrefslogtreecommitdiffstats
path: root/idatui/rpc.py (follow)
Commit message (Collapse)AuthorAgeFilesLines
* projects phase 4: cross-binary back, linkage-guided pre-warm, project verbsblasty2026-07-251-0/+40
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Three things, all following from phase 3 making cross-binary jumps ordinary. **A cross-binary jump was a one-way door.** Nav history is per-binary, so arriving in another binary — a project search hit, or now following an import into the library that implements it — landed you in an empty history with nothing to take you back. _switch_then_goto records the binary it came FROM, and action_back falls through to that hop once local history is spent: Esc walks back through the function you were in, then the binary you were in. Manual Ctrl+O switching records nothing, because that isn't navigation. **Pre-warm follows the linkage graph, not list order.** _prewarm_provider warms the binary providing the most of this one's imports — where a follow is most likely to go, so its startup is paid before you ask for it. "Next in the list" would have been arbitrary; phase 3 gave us something better to ask. pool.prewarm() refuses rather than making room. Evicting a binary the user visited to speculatively load one they haven't is a straight downgrade, and it throws away that binary's caches as well; at a tight budget pre-warm just does nothing. The cost of a worker that doesn't exist yet can only be estimated, so it uses the largest resident one (same program, different database) — and if that estimate proves wrong, the speculative worker is the one evicted, never a chosen one. **Driving a project.** pane spawn --project FILE [--open BIN]; `binaries` lists the inventory (active / resident / indexed / where Esc returns to) and `switch {binary,addr?}` makes another active — with an address it takes the search-hit path, so it records a hop. state gains `binary` and `hops`, which it should have had the moment project mode existed. Verified on real sessions: drive binaries/switch against an echo+cat project pane; Esc crossing back from a switch; and prewarm on echo+libc picking libc (provider of echo's imports) and warming it after an evict. tests: +5 pool (prewarm warms, no-ops when resident, refuses at budget, evicts nothing when refusing, ignores unknown labels) and +4 project UI (jump records the hop, Esc crosses back, hop consumed). Confirmed the Esc-back checks fail with the branch removed. 195/0 scenarios, 27/0 project UI, 36/0 index, 27/0 pool, 33/0 project. Left open: project-level persistence across sessions.
* nav: one notion of "which pane you're in" — delete _prefblasty2026-07-251-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | The app carried three overlapping ideas of the current pane: _active, _pref, and Textual focus. 190e28b tied focus to _active in split; this removes _pref, which turns out never to have been a variable at all. _pref was assigned "listing" in __init__ and "listing" on a project binary switch. Nothing else ever wrote it. But _code_view() branched on it: return self.query_one(DecompView if self._pref == "decomp" else ListingView) so it always returned the listing, whatever you were reading. Its two callers put focus back after the goto prompt closes — so cancelling `g` while in the pseudocode focused the HIDDEN listing, and the pane you were looking at stopped answering the keyboard. Arrows did nothing until you clicked. (It also explains why routing follow through _code_view() earlier made Enter a dead key: the helper had been quietly lying the whole time.) _code_view() now returns the active code pane. _pref is gone from the app, BinaryState and the RPC snapshot keeps "pref" for wire compat, sourced from _code_mode() — the one place that answers "which code view do we return to from hex", and a constant by design in the unified layout. Verified on a live pane both ways: g then Esc in pseudocode, then two Downs. Fixed, the cursor moves 0 -> 2; with the old lookup restored it sits at 0 with focus=ListingView. Same check added to the view_toggle scenario, and it fails without the fix. 195/0, project UI 23/23.
* xrefs: show fine-grained kind (call/jump/read/write/offset) in the dialogblasty2026-07-241-1/+1
| | | | | | | | | | | | | | | | | | ida-pro-mcp's xref_query only classifies xrefs as code/data (xr.iscode). Add an injected `xref_types` tool (server/patch_server.py) that mirrors xref_query's query/envelope shape but derives a fine `kind` from the IDA xref type: call/jump/flow for code (fl_CF/CN/JF/JN/F), read/write/offset/text/info for data (dr_R/W/O/T/I). The worker self-injects it on startup like the other custom tools. * domain: Xref gains a `kind` field; _parse_xrefs reads it; xrefs_to() now calls xref_types (falling back to xref_query if absent). xrefs_from is unchanged. * app: the `x` dialog shows the kind as an aligned column after the address (`000034F4 read sub_34F0+0x4`). * rpc: the structured xrefs_to read carries `kind` too. Verified live over the worker/RPC harness on targets/echo: sub_2C00 callers -> call; __progname -> offset (GOT), read (sub_34F0), write (sub_3500); stdout -> read/offset.
* unify: the continuous listing is the one code view; deprecate DisasmViewblasty2026-07-231-4/+2
| | | | | | | | | | | | | | | | | | | | Full IDA-style unification. The function-bounded DisasmView is gone from the UI: navigation opens ONE continuous segment listing (functions+data+undefined interleaved) at the target; F5/Tab decompiles the function under the cursor and back. The listing gained the opcode column (Head.raw, 'o' toggle) so rendering matches disasm. Routing: _do_navigate/_open_function/_open_entry target the listing; _show_active drops disasm; _active in {listing,decomp,hex}; _pref=listing. Decomp is a per-function toggle with a listing fallback on failure. Edits reload in place (_reload_active_code); bump_names() drops the listing cache so renames refresh. Listing 'n' is symbol-aware. ListingModel gains cached_line/lines shims; Head.label. DisasmView removed from compose/handlers; rpc updated. DisasmModel kept (domain/test_domain). Tests migrated (c.dis->ListingView; open(decomp) F5s; xref/search re-pointed). Per-scenario green across view_toggle/rename/follow_xrefs/xref_labels/decomp_*/ search/mouse/startup/continuous_view (30/1, 1 cold-decompile flake).
* app: ListingView — virtualized flat code+data listing for regions (M2)blasty2026-07-221-1/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | The real disassembly-listing view. ListingView is a line-virtualized ScrollView (reusing ColumnCursor/SearchMixin/NavMixin) backed by ListingModel, rendering code, data (db/dw/dd, strings, jump tables) and undefined bytes interleaved with per-kind styling. Non-function regions now open in it instead of the M0 DisasmModel stopgap. Integration (IdaTui): a new `listing` active mode. _open_entry routes region entries to ListingView; _show_active/compose add it; follow/xrefs/comment and the c/p/u edit verbs handle it (define_func upgrades a region straight to the function view); backslash reaches hex and returns via _code_mode (so leaving hex from a region lands back on the listing, not a func view); Tab explains there's no pseudocode for a region. rpc._active_widget learns `listing`. Server fix (the important one): the `heads` walker now steps by get_item_end instead of next_head. next_head SKIPS undefined bytes, so after undefining a function its start address wasn't even a listing line and the cursor snapped to the next defined item; stepping by item-end renders undefined bytes as `db ?` lines (IDA-accurate) and makes navigation land exactly on any address — essential for "go to unmarked bytes and hit c". Needs a supervisor restart. Tests: region_define rewritten to assert the ListingView path + segment-wide listing + p-upgrade; new listing_view scenario (data-segment listing renders data heads, cursor reports head ea, backslash->hex->back). Pilot 115 pass / 1 pre-existing flaky (filter); rpc_smoke 29/0; test_domain 27/0.
* pane: reap leaked idalib workers; drive: friendlier name resolution (fixes ↵user2026-07-121-1/+7
| | | | spawn-hang + bare KeyError)
* fix: don't hang drive pc on undecompilable functionsuser2026-07-111-1/+20
| | | | | | | | | | | | | | | | toggle_view's settle predicate (lambda: app._active != before) never fired when tabbing toward pseudocode on a function Hex-Rays can't decompile: App._apply_decomp snaps the view back to disasm, so _active returns to its prior value -> full 20s settle timeout (x2 in _show_decomp, ~40s for drive pc). Recognize the decomp-failed fallback as settled. Also harden two amplifiers surfaced by the same case: - rpcclient: the CLI socket had no read timeout and would block forever on any server slowness; add a bounded settimeout (IDATUI_RPC_TIMEOUT, default 90s) with a clear error. - domain.decompile: pass a bounded 15s timeout and cache failures, so a failing decompile can't sit at the 30s client default or be re-run by transport retries.
* app: drop the Header (keep the Footer); rpc: function-scope xrefs_fromblasty2026-07-101-2/+19
| | | | | | | | | | | | | | The clock/title Header added noise (and made screen() non-deterministic); remove it from the app itself — the Footer stays. Layout shifts up a row; scenario suite still 101 green. xrefs_from on a function was near-useless: the server's xrefs_from is address-scoped, so passing the entry ea only returned the fall-through from the first instruction. For a function target it now returns whole-body references from the decompiler (callees + string/data refs: {to,name,string,is_func,type}); an explicit 0xADDR stays address-scoped. Verified live: xrefs_from main now lists setlocale/getopt_long/sub_* etc. rpc_smoke: 25 green.
* rpc: cursor_on + word= edits, single-driver gate, colored screenblasty2026-07-101-8/+83
| | | | | | | | | | | | | | cursor_on {word,line?,occurrence?} places the cursor on a token, verified with the app's own tokenizer (so 'main' won't match inside 'domain'); rename/retype/follow gain an optional word= that runs it first — the ergonomic way to edit a named symbol without a manual view+cursor dance. Single-driver gate: the socket now serves one client at a time; a second concurrent connection is refused with 'busy' (no multi-driver support yet, by request). Sequential connections are unaffected. screen gains format=text|html|svg (html/svg are colored — for an out-of-band web viewer). rpc_smoke: 24 green.
* rpc: readiness, quit, methods discovery, not-ready guardblasty2026-07-101-2/+71
| | | | | | | | | | | Spawn-and-drive needs to know when the freshly-launched TUI is usable: ping/state now carry {ready,functions,complete} (cheap; no health call) and ping adds the module name. Add a graceful 'quit' verb (acks, then exits on a short delay so the reply still flushes) and a self-documenting 'methods' table. Program-dependent verbs are refused with a clear 'not ready' instead of exploding in a worker while still loading. rpc_smoke covers readiness, methods and quit (21 green).
* rpc: more verbs — structured reads, modal select, in-view search, saveblasty2026-07-101-0/+106
| | | | | | | | | | | | Introspection (offloaded to a thread so a big fetch can't freeze the render): pseudocode (full body), disassembly (bounded), xrefs_to/from (structured), resolve (name->ea). These let an agent reason about a function and the call graph without scraping screen(). Semantic: select (choose the highlighted/nth item in the open xrefs or symbol-palette list and activate it — the natural follow-up to xrefs/ symbols), search (/ or ? incremental in the active code view), save (Ctrl+S). rpc_smoke exercises all of them end-to-end (18 green).
* rpc: semantic verbs (typed-with-delay ops + fast movement)blasty2026-07-101-1/+122
| | | | | | | | | | | | | | Layer high-level verbs over the raw injection: goto/open, rename, comment, retype, follow, back, toggle_view, hex, xrefs, symbols, structs, close, plus fast move/cursor. Editing/goto verbs type through the real prompts with a per-char delay (default 35ms) so viewers see it typed; movement uses bare keypresses with a pump-only settle to stay snappy. Each verb settles on an op-specific predicate where one exists (goto lands on the target, follow grows the nav stack, toggle flips _active, modals open), so the returned state is never stale. rpc_smoke now drives the semantic path end-to-end (rename round-trip via the prompt-fill seam), 11 green.
* rpc: unix-socket puppeteering server (raw keys + introspection + screen)blasty2026-07-101-0/+273
Run the TUI with --rpc <sock> and it renders normally while an asyncio listener on the same loop lets another process drive it. Handlers touch the UI directly (same loop, no thread hop), so every injected key has the identical on-screen effect a keyboard would — the point for livestreaming. Newline-delimited JSON. v1 methods: keys/text (raw injection, via the same _press_keys the pilot uses; text can interleave wait:<ms> for a typed-out look), state/view/screen/functions (introspection; screen() is a full plain-text render of exactly what the viewer sees). No auth by design — the socket is 0600, local only. tests/rpc_smoke.py drives it end-to-end over a real socket (7 green).