aboutsummaryrefslogtreecommitdiffstats
path: root/experiments/graph_shot.py
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-08-06 15:11:54 +0200
committerblasty <blasty@local>2026-08-06 15:11:54 +0200
commit4fbd6b61eaa3461db673b220e2376b3d1e922a9b (patch)
tree6b8251b47b075c7c0f14569549c19abbf22656c7 /experiments/graph_shot.py
parenttests: graph scenarios (diff)
downloadida-tui-4fbd6b61eaa3461db673b220e2376b3d1e922a9b.tar.gz
ida-tui-4fbd6b61eaa3461db673b220e2376b3d1e922a9b.tar.xz
ida-tui-4fbd6b61eaa3461db673b220e2376b3d1e922a9b.zip
graph: docs, and the offline layout tools
cfg_dump freezes real CFGs to JSON; graph_spike renders one or --stats a whole corpus through the shipping engine; graph_smoke is the end-to-end tool->domain->layout check; graph_shot renders the real view headless at a chosen size, which is the only sane way to judge it (a tiled pane is far too narrow and the minimap sits on top of the graph).
Diffstat (limited to 'experiments/graph_shot.py')
-rw-r--r--experiments/graph_shot.py66
1 files changed, 66 insertions, 0 deletions
diff --git a/experiments/graph_shot.py b/experiments/graph_shot.py
new file mode 100644
index 0000000..5f0d45a
--- /dev/null
+++ b/experiments/graph_shot.py
@@ -0,0 +1,66 @@
+"""Render the graph view headless at a chosen size and print it.
+
+ ~/ida-venv/bin/python experiments/graph_shot.py [func] [cols] [rows] [zoom]
+
+The pane a person runs this in is usually too small to judge the layout, and the
+pilot lays out synchronously at whatever size you ask for -- so this is the way
+to actually look at the thing.
+"""
+import asyncio
+import os
+import shutil
+import sys
+
+REPO = os.path.expanduser("~/dev/ida-tui-maybe")
+sys.path.insert(0, REPO)
+os.chdir(REPO)
+
+func = sys.argv[1] if len(sys.argv) > 1 else "sub_2297"
+cols = int(sys.argv[2]) if len(sys.argv) > 2 else 170
+rows = int(sys.argv[3]) if len(sys.argv) > 3 else 55
+zoom = int(sys.argv[4]) if len(sys.argv) > 4 else 0
+
+src, tmp = f"{REPO}/targets/echo", "/tmp/echo_shot"
+shutil.copy(src, tmp)
+for e in ".i64 .id0 .id1 .id2 .nam .til".split():
+ try:
+ os.remove(tmp + e)
+ except OSError:
+ pass
+
+from idatui.app import IdaTui, GraphView # noqa: E402
+from idatui._sync import wait_for # noqa: E402
+from idatui.rpc import screen_text # noqa: E402
+
+
+async def main() -> None:
+ app = IdaTui(tmp, keepalive=False)
+ async with app.run_test(size=(cols, rows)) as pilot:
+ await wait_for(lambda: app.program is not None and app._cur is not None,
+ pilot.pause, 120)
+ ea = app.program.resolve(func)
+ fn = app.program.function_of(ea)
+ app._open_function(fn.addr, fn.name)
+ await wait_for(lambda: app._cur is not None and app._cur.ea == fn.addr,
+ pilot.pause, 60)
+ await pilot.pause(0.2)
+ await pilot.press("space")
+ gv = app.query_one(GraphView)
+ ok = await wait_for(lambda: app._active == "graph" and gv.lay is not None,
+ pilot.pause, 90)
+ if not ok:
+ print("graph never opened:", app.query_one("#status").render())
+ return
+ for _ in range(zoom):
+ await pilot.press("z")
+ await pilot.pause(0.2)
+ await pilot.pause(0.4)
+ print(screen_text(app)["text"])
+ print()
+ print("status:", app.query_one("#status").render())
+ print("stats :", gv.lay.stats, "canvas",
+ f"{gv.lay.width}x{gv.lay.height}", "zoom", gv.ZOOMS[gv._zoom])
+ app._save_on_exit = False
+
+
+asyncio.run(main())