aboutsummaryrefslogtreecommitdiffstats
path: root/experiments/graph_spike.py
diff options
context:
space:
mode:
authorblasty <peter@haxx.in>2026-08-21 12:14:46 +0200
committerblasty <peter@haxx.in>2026-08-21 12:15:15 +0200
commit02d02417800184fb76cd0245cdaa94c437aa4081 (patch)
tree7589e6e2e8426bb7370fc56eee52d1559c9d7e57 /experiments/graph_spike.py
parentadopt ruff: pinned formatter + import sorting, opt-in pre-commit hook (diff)
downloadida-tui-02d02417800184fb76cd0245cdaa94c437aa4081.tar.gz
ida-tui-02d02417800184fb76cd0245cdaa94c437aa4081.tar.xz
ida-tui-02d02417800184fb76cd0245cdaa94c437aa4081.zip
reformat: ruff format + import sort, mechanically (see ruff.toml)
No behavior. Listed in .git-blame-ignore-revs (next commit).
Diffstat (limited to 'experiments/graph_spike.py')
-rw-r--r--experiments/graph_spike.py68
1 files changed, 48 insertions, 20 deletions
diff --git a/experiments/graph_spike.py b/experiments/graph_spike.py
index e8cf3b8..b3c5dc2 100644
--- a/experiments/graph_spike.py
+++ b/experiments/graph_spike.py
@@ -11,6 +11,7 @@ milliseconds when you're changing layout heuristics.
python3 experiments/graph_spike.py /tmp/cfg-echo.json --func sub_61D0
python3 experiments/graph_spike.py /tmp/cfg-echo.json --stats
"""
+
from __future__ import annotations
import argparse
@@ -23,8 +24,10 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from idatui import graph as G # noqa: E402
COLOR = {
- G.E_UNCOND: "\033[38;5;39m", G.E_TRUE: "\033[38;5;40m",
- G.E_FALSE: "\033[38;5;203m", G.E_SWITCH: "\033[38;5;178m",
+ G.E_UNCOND: "\033[38;5;39m",
+ G.E_TRUE: "\033[38;5;40m",
+ G.E_FALSE: "\033[38;5;203m",
+ G.E_SWITCH: "\033[38;5;178m",
G.E_BACK: "\033[38;5;135m",
}
DIM, RESET = "\033[38;5;244m", "\033[0m"
@@ -37,11 +40,19 @@ def build(rec: dict, max_lines: int):
for b in rec["blocks"]:
lines = list(b["lines"])
if max_lines and len(lines) > max_lines:
- lines = lines[:max_lines - 1] + [f"... {len(b['lines']) - max_lines + 1} more"]
+ lines = lines[: max_lines - 1] + [
+ f"... {len(b['lines']) - max_lines + 1} more"
+ ]
texts[b["id"]] = lines
- blocks = [G.Block(id=b["id"], start=b["start"], end=b["end"],
- succs=[(d, k) for d, k in b["succs"]])
- for b in rec["blocks"]]
+ blocks = [
+ G.Block(
+ id=b["id"],
+ start=b["start"],
+ end=b["end"],
+ succs=[(d, k) for d, k in b["succs"]],
+ )
+ for b in rec["blocks"]
+ ]
def sizer(b: G.Block) -> tuple[int, int]:
lines = texts[b.id]
@@ -57,7 +68,8 @@ def render(lay: G.Layout, texts: dict[int, list[str]], color: bool) -> str:
for row in range(lay.height):
cells: dict[int, tuple[str, str]] = {}
for col, (ch, kind, _eid) in lay.painting.cells_at_row(
- row, 0, lay.width).items():
+ row, 0, lay.width
+ ).items():
cells[col] = (ch, COLOR.get(kind, ""))
for n in lay.nodes_at_row(row):
label = f"loc_{n.block.start:X}" if n.block else ""
@@ -107,34 +119,48 @@ def main() -> int:
ap.add_argument("corpus")
ap.add_argument("--func", help="function name (default: the smallest)")
ap.add_argument("--stats", action="store_true", help="lay out the whole corpus")
- ap.add_argument("--max-lines", type=int, default=8,
- help="collapse blocks longer than this (0 = never)")
+ ap.add_argument(
+ "--max-lines",
+ type=int,
+ default=8,
+ help="collapse blocks longer than this (0 = never)",
+ )
ap.add_argument("--no-color", action="store_true")
- ap.add_argument("--engine", choices=G.ENGINES, default=None,
- help="layout backend (default: $IDATUI_GRAPH_ENGINE or auto)")
+ ap.add_argument(
+ "--engine",
+ choices=G.ENGINES,
+ default=None,
+ help="layout backend (default: $IDATUI_GRAPH_ENGINE or auto)",
+ )
args = ap.parse_args()
recs = json.load(open(args.corpus))
if args.stats:
- print(f"{'blocks':>7} {'nodes':>6} {'dummy':>6} {'layer':>6} "
- f"{'canvas':>12} {'ms':>8} name")
+ print(
+ f"{'blocks':>7} {'nodes':>6} {'dummy':>6} {'layer':>6} "
+ f"{'canvas':>12} {'ms':>8} name"
+ )
tot = 0.0
for r in sorted(recs, key=lambda r: len(r["blocks"])):
blocks, sizer, _ = build(r, args.max_lines)
lay = G.layout(blocks, sizer, engine=args.engine)
s = lay.stats
tot += s["ms"]
- print(f"{s['blocks']:>7} {s['nodes']:>6} {s['dummies']:>6} "
- f"{s['layers']:>6} {lay.width:>5}x{lay.height:<6} "
- f"{s['ms']:>8.1f} {r['name']}")
+ print(
+ f"{s['blocks']:>7} {s['nodes']:>6} {s['dummies']:>6} "
+ f"{s['layers']:>6} {lay.width:>5}x{lay.height:<6} "
+ f"{s['ms']:>8.1f} {r['name']}"
+ )
print(f"total {tot:.0f} ms over {len(recs)} functions")
return 0
if args.func:
rec = next((r for r in recs if r["name"] == args.func), None)
if rec is None:
- print("no such function; have: "
- f"{', '.join(r['name'] for r in recs[:20])}", file=sys.stderr)
+ print(
+ f"no such function; have: {', '.join(r['name'] for r in recs[:20])}",
+ file=sys.stderr,
+ )
return 1
else:
rec = min(recs, key=lambda r: len(r["blocks"]))
@@ -142,8 +168,10 @@ def main() -> int:
blocks, sizer, texts = build(rec, args.max_lines)
lay = G.layout(blocks, sizer, engine=args.engine)
print(render(lay, texts, color=not args.no_color))
- print(f"\n{rec['name']}: {lay.stats} canvas {lay.width}x{lay.height}",
- file=sys.stderr)
+ print(
+ f"\n{rec['name']}: {lay.stats} canvas {lay.width}x{lay.height}",
+ file=sys.stderr,
+ )
return 0