diff options
| author | user <user@clank> | 2026-08-07 02:30:29 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-07 02:30:29 +0200 |
| commit | 870f89eac58fa923d1991ec0b06fda8230dc0116 (patch) | |
| tree | 8a74e6fc0d809ef0e6dc95858e1092d585f673db /idatui | |
| parent | RE-BASELINE on a corrected benchmark. phase_graph was measuring a cache hit: ... (diff) | |
| download | ida-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.py | 35 |
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 |
