Files
browser/orderfile/tools/hotlist.py
T

61 lines
2.6 KiB
Python
Executable File

#!/usr/bin/env python3
# usage: hotlist.py <binary> <resident.json> <out-prefix>
# Emits <out>.text and <out>.rodata: symbols overlapping resident pages, in address order,
# plus a per-section summary of resident bytes.
import sys, json, re, subprocess, bisect, collections, os
binary, resjson, outp = sys.argv[1:4]
syms = []
# "addr size type name", but the size is omitted for sizeless symbols and a
# Zig instantiation's name can contain spaces: splitting on whitespace drops
# both (~70 symbols per profile).
nm_line = re.compile(r"^([0-9a-fA-F]+) (?:([0-9a-fA-F]+) )?(.) (.+)$")
for line in subprocess.run(["nm", "-S", "--numeric-sort", "--defined-only", binary], capture_output=True, text=True).stdout.splitlines():
m = nm_line.match(line)
if m and m.group(3) in "TtWwVvRrDdBb":
syms.append((int(m.group(1), 16), int(m.group(2) or "0", 16), m.group(3), m.group(4)))
syms.sort()
starts = [s[0] for s in syms]
secs = []
for line in subprocess.run(["readelf", "-SW", binary], capture_output=True, text=True).stdout.splitlines():
if not line.strip().startswith("[") or "]" not in line: continue
p = line.split("]", 1)[1].split()
try:
secs.append((int(p[2], 16), int(p[4], 16), p[0]))
except (IndexError, ValueError): pass
secs = [s for s in secs if s[0]]
secs.sort()
sec_starts = [s[0] for s in secs]
def sec_of(va):
i = bisect.bisect_right(sec_starts, va) - 1
if i >= 0 and va < secs[i][0] + secs[i][1]: return secs[i][2]
return "?"
res = json.load(open(resjson))
exe = os.path.realpath(binary)
hot = {"text": collections.OrderedDict(), "rodata": collections.OrderedDict()}
per_sec = collections.Counter()
for m in res:
if os.path.realpath(m["path"]) != exe: continue
for pi in m["present"]:
va = m["lo"] + pi * 4096
sec = sec_of(va)
per_sec[sec] += 4
kind = "text" if sec in (".text", ".text.hot") else ("rodata" if sec in (".rodata", ".rodata.hot") else None)
if not kind: continue
i = bisect.bisect_right(starts, va + 4095) - 1
while i >= 0:
a, sz, t, n = syms[i]
end = a + max(sz, 1)
if end <= va and sz: break
if a >= va + 4096: i -= 1; continue
if end > va or (not sz and a >= va):
hot[kind][n] = a
i -= 1
if a < va - 65536: break
for kind in hot:
names = sorted(hot[kind], key=lambda n: hot[kind][n])
with open(f"{outp}.{kind}", "w") as f:
f.write("\n".join(names) + "\n")
print(f"{kind}: {len(names)} hot symbols")
for sec, kb in per_sec.most_common(12):
print(f" {kb:7d} KB {sec}")