diff options
| -rw-r--r-- | .auto/bench.py | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/.auto/bench.py b/.auto/bench.py index 4074838..b4f5793 100644 --- a/.auto/bench.py +++ b/.auto/bench.py @@ -322,6 +322,71 @@ async def phase_split(app, pilot, funcs): note("split_mapped_lines", mapped) +async def phase_rename(app, pilot, funcs): + """Rename a function and get the listing back — the commonest RE operation. + + A rename invalidates cached names everywhere, and the question this measures + is what the *listing* then costs: the cursor has to land back on the same row + with the new name showing. Renames are undone afterwards so the .i64 the + bench stages from is never left edited. + """ + lst = app.query_one(ListingView) + ed = app.program + done = 0 + spent = 0.0 + tag = f"_bench_{os.getpid()}" + for k, fn in enumerate(funcs[:6]): + app._open_function(fn.addr, fn.name) + if not await _wait(pilot, lambda fn=fn: app._cur is not None + and app._cur.ea == fn.addr and lst.total > 0 + and lst._cursor_ea() == fn.addr, 120): + fail(f"rename:open:{fn.name}") + continue + row = lst.cursor + new = f"{tag}_{k}" + t0 = time.perf_counter() + try: + ed.client.call("rename", + batch={"func": [{"addr": hex(fn.addr), "name": new}]}) + except Exception: # noqa: BLE001 + fail(f"rename:call:{fn.name}") + continue + ed.bump_names() + # What the app does next: reopen the listing where it was and paint it. + lm = ed.listing(fn.addr) + idx = max(lm.ensure_ea(fn.addr), 0) if lm is not None else 0 + lst.load(lm, new, cursor=idx, scroll_y=max(idx - 6, 0)) + got = await _wait(pilot, lambda fn=fn: lst.total > 0 + and lst._cursor_ea() == fn.addr, 120) + await pilot.pause(0) + _paint(lst) + spent += (time.perf_counter() - t0) * 1000 + # The name lives on the `proc` banner row, a few rows above the code row + # ensure_ea lands on (banner rows aren't address-indexed), so look at the + # window rather than the single row. + shows_new = False + if lm is not None: + for k2 in range(max(idx - 4, 0), idx + 2): + h = lm.get(k2) + if h is not None and (new in (h.text or "") + or new == (h.name or "")): + shows_new = True + break + if not (got and idx == row and shows_new): + fail(f"rename:{fn.name}:row={idx}/{row} shows_new={shows_new}") + else: + done += 1 + try: # put it back, whatever happened above + ed.client.call("rename", + batch={"func": [{"addr": hex(fn.addr), + "name": fn.name}]}) + ed.bump_names() + except Exception: # noqa: BLE001 + fail(f"rename:undo:{fn.name}") + TIMES.setdefault(PREFIX + "rename_ms", []).append(spent) + note("rename_ok", done) + + async def phase_search(app, pilot, funcs, terms): """The real incremental-search path: search_begin, then one search_update per typed character, exactly as the Input's on_changed drives it.""" @@ -500,6 +565,7 @@ async def run_target(binary, reps, nfuncs, pages, frames, terms, skip=0): await phase_decomp(app, pilot, big) await phase_graph(app, pilot, big) if first: + await phase_rename(app, pilot, big) await phase_split(app, pilot, big) await phase_search(app, pilot, big, terms) await phase_index(app, pilot, big) |
