aboutsummaryrefslogtreecommitdiffstats
path: root/idatui
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-07 02:30:29 +0200
committeruser <user@clank>2026-08-07 02:30:29 +0200
commit870f89eac58fa923d1991ec0b06fda8230dc0116 (patch)
tree8a74e6fc0d809ef0e6dc95858e1092d585f673db /idatui
parentRE-BASELINE on a corrected benchmark. phase_graph was measuring a cache hit: ... (diff)
downloadida-tui-870f89eac58fa923d1991ec0b06fda8230dc0116.tar.gz
ida-tui-870f89eac58fa923d1991ec0b06fda8230dc0116.tar.xz
ida-tui-870f89eac58fa923d1991ec0b06fda8230dc0116.zip
Fetch a graph's listing rows from the blocks' MERGED EXTENTS, not their convex hull, and assign them per block by bisect. IDA puts a function's cold/tail chunks far from its entry, so the hull of a 1.4KB function could be 680KB wide: it fetched 128k rows, took 3s, and still came back EMPTY for the far blocks because the pager's 64-page bound ran out first. lg_graph 9561 -> 1022.
Result: {"status":"keep","total_ms":19062.1,"lg_boot_ms":751.3,"lg_decomp_ms":2605.7,"lg_graph_ms":1022,"lg_hex_ms":554.5,"lg_index_ms":103.9,"lg_listing_cold_ms":545,"lg_listing_warm_ms":405.6,"lg_nav_ms":6636.8,"lg_palette_ms":4.7,"lg_render_ms":212.2,"lg_search_ms":2269,"pure_graph_ms":239.9,"sm_boot_ms":538.5,"sm_decomp_ms":688.4,"sm_graph_ms":712.7,"sm_hex_ms":557.5,"sm_index_ms":0,"sm_listing_cold_ms":259.9,"sm_listing_warm_ms":260.2,"sm_nav_ms":363.8,"sm_palette_ms":0.3,"sm_render_ms":252.7,"sm_search_ms":77.5,"fails":0}
Diffstat (limited to 'idatui')
-rw-r--r--idatui/domain.py35
1 files changed, 32 insertions, 3 deletions
diff --git a/idatui/domain.py b/idatui/domain.py
index 8b423e3..9dea353 100644
--- a/idatui/domain.py
+++ b/idatui/domain.py
@@ -1759,10 +1759,13 @@ class Program:
return None
f = payload.get("func") or {}
lo = min(b.start for b in blocks)
- hi = max(b.end for b in blocks)
- rows = self._heads_between(lo, hi)
+ rows = self._block_rows(blocks)
+ eas = [h.ea for h in rows]
for b in blocks:
- b.rows = [h for h in rows if b.start <= h.ea < b.end]
+ # bisect, not a scan per block: a 400-block function against a few
+ # thousand rows is a million comparisons done for nothing.
+ b.rows = rows[bisect.bisect_left(eas, b.start):
+ bisect.bisect_left(eas, b.end)]
fcv = Flowchart(
func_ea=_as_int(f.get("addr", lo)),
name=str(f.get("name") or f"sub_{lo:X}"),
@@ -1773,6 +1776,32 @@ class Program:
self._flowcharts[key] = (fcv, gen)
return fcv
+ #: Bytes of padding between two blocks that are still worth fetching in one
+ #: call. Alignment gaps are a few bytes; a function chunk is far away.
+ _BLOCK_GAP = 256
+
+ def _block_rows(self, blocks: list[BasicBlock]) -> list[Head]:
+ """Listing rows covering ``blocks``, address-ordered.
+
+ Fetches the blocks' merged extents, NOT their convex hull. IDA puts a
+ function's cold/tail chunks a long way from its entry, so the hull of a
+ 1.4 KB function can be 680 KB wide: walking it fetched 128 000 listing
+ rows and took three seconds to draw a graph, all but 300 of them thrown
+ away immediately. Adjacent blocks coalesce, so an ordinary contiguous
+ function is still exactly one call.
+ """
+ spans: list[list[int]] = []
+ for start, end in sorted((b.start, b.end) for b in blocks):
+ if spans and start <= spans[-1][1] + self._BLOCK_GAP:
+ if end > spans[-1][1]:
+ spans[-1][1] = end
+ else:
+ spans.append([start, end])
+ out: list[Head] = []
+ for start, end in spans:
+ out.extend(self._heads_between(start, end))
+ return out
+
def _heads_between(self, lo: int, hi: int) -> list[Head]:
"""Listing rows for [lo, hi), paged. Same tool and same ``Head`` shape
the listing view renders, so the graph inherits IDA's colour tags and