diff options
| author | user <user@clank> | 2026-08-07 06:45:52 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-07 06:45:52 +0200 |
| commit | d9e8fdb79136dfa32e01631aed8917c1cb9c1b54 (patch) | |
| tree | 53c4e6c440f8fa5ee19f2e8ae5b3a749ab28583b /server/patch_server.py | |
| parent | Size the worker's per-line render cache to hold a segment's DISTINCT lines (1... (diff) | |
| download | ida-tui-d9e8fdb79136dfa32e01631aed8917c1cb9c1b54.tar.gz ida-tui-d9e8fdb79136dfa32e01631aed8917c1cb9c1b54.tar.xz ida-tui-d9e8fdb79136dfa32e01631aed8917c1cb9c1b54.zip | |
heads(digest=True): the worker answers "does this page still render exactly as you hold it?" with a hash and a count instead of the page. After a rename nearly every page is unchanged, so the pickling, transfer, unpickling and Head rebuild are all skipped. Redone at PAGE granularity end to end, which fixes both bugs of the first attempt. lg_search 5628 -> 3959.
Result: {"status":"keep","total_ms":26491.7,"lg_boot_ms":808.6,"lg_decomp_ms":2600.2,"lg_graph_ms":901.2,"lg_hex_ms":448.7,"lg_index_ms":70.3,"lg_listing_cold_ms":432.8,"lg_listing_warm_ms":445.5,"lg_nav_ms":7004.8,"lg_palette_ms":4.8,"lg_rename_ms":758.6,"lg_render_ms":231.3,"lg_search_ms":3959.2,"lg_split_ms":2659.2,"pure_graph_ms":214.7,"sm_boot_ms":432.6,"sm_decomp_ms":1305.1,"sm_graph_ms":758.3,"sm_hex_ms":431.8,"sm_index_ms":2.3,"sm_listing_cold_ms":275.4,"sm_listing_warm_ms":289.4,"sm_nav_ms":295.6,"sm_palette_ms":0.3,"sm_rename_ms":424.3,"sm_render_ms":263.6,"sm_search_ms":61.1,"sm_split_ms":1411.9,"fails":0}
Diffstat (limited to 'server/patch_server.py')
| -rw-r--r-- | server/patch_server.py | 35 |
1 files changed, 34 insertions, 1 deletions
diff --git a/server/patch_server.py b/server/patch_server.py index f3cbe35..a34a4a9 100644 --- a/server/patch_server.py +++ b/server/patch_server.py @@ -576,6 +576,28 @@ def _idatui_spans(line): return [[k, t] for k, t, _o in out], trimmed +def _idatui_rows_digest(rows): + """A value that changes whenever any of ``rows`` would render differently. + + Covers everything a client keeps off a row: address, kind, size, the plain + text, the symbol name and the colour spans (which is what makes it exact + rather than a heuristic -- two lines can collapse to the same text and still + be coloured differently). + + Uses the interpreter's own ``hash``, deliberately. It never has to mean + anything outside this process: the client stores what a page hashed to when + it loaded it and hands the same number back to ask whether the page still + hashes to that. One worker, one process, one hash seed. + """ + acc = 0 + for r in rows: + sp = r.get("spans") + acc = hash((acc, r.get("ea"), r.get("kind"), r.get("size"), + r.get("text"), r.get("name"), + tuple(map(tuple, sp)) if sp else None)) + return acc + + def _idatui_unknown_row(ea, size): """One collapsed row for a run of ``size`` undefined bytes starting at ``ea``. A single byte is rendered normally (shows its value); a longer run @@ -661,6 +683,7 @@ def heads( end: Annotated[str, "Optional exclusive end address; default = segment end"] = "", back: Annotated[bool, "Walk backwards: return the count heads ENDING just before addr, in forward order"] = False, annotate: Annotated[bool, "Emit IDA-style function boundary banner rows (kind sep/funchdr)"] = False, + digest: Annotated[bool, "Return only a digest+count of the rows, not the rows themselves"] = False, ) -> dict: """Walk item heads from ``addr`` as a flat listing: every head is rendered (code OR data OR undefined) via generate_disasm_line and stepped with @@ -767,7 +790,17 @@ def heads( rows.extend(_rows_for(ea)) # a struct head expands into member rows ea = _advance(ea) cursor = {"next": hex(ea)} if more else {"done": True} - return {"addr": str(addr), "heads": rows, "cursor": cursor} + out = {"addr": str(addr), "cursor": cursor, + "digest": _idatui_rows_digest(rows), "count": len(rows)} + # ``digest`` mode answers "is this page still exactly what you have?" without + # shipping it. The rows are built either way -- generate_disasm_line is the + # floor and there is no way to know a line is unchanged without rendering it + # -- but pickling several hundred rows with their colour spans, unpickling + # them and rebuilding Heads is about 40% of what a page costs, and after a + # rename almost every page comes back identical. + if not digest: + out["heads"] = rows + return out @tool |
