Files
soothe2-re/rtcapture2.py
T

129 lines
4.5 KiB
Python

#!/usr/bin/env python3
"""rtcapture2.py — robust runtime capture for a LONG render (host lives minutes).
Dumps sections + raw target buffers at module_base+rva (PE ImageBase=0x180000000).
"""
import subprocess, time, glob, os, struct, sys
RPP = sys.argv[1] if len(sys.argv) > 1 else '/home/m/soothe-bt/dual300.rpp'
OUT = sys.argv[2] if len(sys.argv) > 2 else '/tmp/rtcap2.log'
RVAS = [0x541134, 0x541258, 0x541278, 0x5413c8, 0x541298, 0x541470, 0x541480, 0x541488, 0x5414b0]
def find_pe(pid):
mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY)
seen = set()
for line in open(f'/proc/{pid}/maps').read().splitlines():
lo = int(line.split()[0].split('-')[0], 16)
if lo in seen:
continue
seen.add(lo)
try:
hdr = os.pread(mem, 0x1000, lo)
except Exception:
continue
if hdr[:2] != b'MZ':
continue
try:
x = struct.unpack('<I', hdr[0x3c:0x40])[0]
if hdr[x:x + 4] != b'PE\x00\x00':
continue
magic = struct.unpack('<H', hdr[x + 24:x + 26])[0]
ib = struct.unpack('<Q', hdr[x + 48:x + 56])[0] if magic == 0x20b else struct.unpack('<I', hdr[x + 52:x + 56])[0]
return lo, ib
except Exception:
continue
os.close(mem)
return None, 0
def sections(pid, base):
mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY)
hdr = os.pread(mem, 0x1000, base)
x = struct.unpack('<I', hdr[0x3c:0x40])[0]
nsec = struct.unpack('<H', hdr[x + 6:x + 8])[0]
opt = struct.unpack('<H', hdr[x + 20:x + 22])[0]
out = []
for i in range(nsec):
sh = os.pread(mem, 40, base + x + 24 + opt + i * 40)
nm = sh[:8].rstrip(b'\0').decode('utf8', 'replace')
vsize, va, rsize, roc = struct.unpack('<IIII', sh[8:24])
ch = struct.unpack('<I', sh[36:40])[0]
out.append((nm, va, vsize, ch))
os.close(mem)
return out
def vst_base(pid):
lo = None
for line in open(f'/proc/{pid}/maps').read().splitlines():
if 'soothe2' not in line:
continue
a = int(line.split()[0].split('-')[0], 16)
lo = a if lo is None else min(lo, a)
if lo is None:
return None
try:
mem0 = os.open(f'/proc/{pid}/mem', os.O_RDONLY)
hdr = os.pread(mem0, 0x1000, lo)
os.close(mem0)
except Exception:
return None
return lo if hdr[:2] == b'MZ' else None
def main():
out = open(OUT, 'w')
proc = subprocess.Popen(['reaper', '-nosplash', '-renderproject', RPP], stdout=out, stderr=subprocess.STDOUT)
t0 = time.time()
base = None
host = None
while time.time() - t0 < 90:
for pid in glob.glob('/proc/[0-9]*'):
pid = int(os.path.basename(pid))
try:
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().decode('utf8', 'replace')
maps = open(f'/proc/{pid}/maps').read()
except Exception:
continue
if 'yabridge-host' not in cmd and 'soothe2' not in maps:
continue
b = vst_base(pid)
if b:
host, base = pid, b
print(f'pid={pid} vst_base={hex(b)}', flush=True)
break
if base:
break
time.sleep(0.05)
if not base:
print('NO PE FOUND', flush=True)
proc.kill()
return
for nm, va, vs, ch in sections(host, base):
print(f'sec {nm:8s} rva={hex(va):>9} vsize={hex(vs):>9} chars={hex(ch)}', flush=True)
mem = os.open(f'/proc/{host}/mem', os.O_RDONLY)
t3 = time.time()
last = 0
while proc.poll() is None and time.time() - t3 < 60:
try:
d = os.pread(mem, 0x20, base + 0x541258)
win = struct.unpack('<8f', d[:32]) if len(d) >= 32 else None
except Exception:
win = None
now = time.time() - t3
if now - last >= 2.0 or win is None:
print(f'sample t={now:.1f}s win[0:8]={[round(v,5) for v in win] if win else None}', flush=True)
for rva in RVAS:
d = os.pread(mem, 128, base + rva)
fl = struct.unpack('<%df' % (len(d) // 4), d[:len(d) // 4 * 4]) if len(d) >= 4 else []
print(f' {hex(rva)} floats={[round(v,5) for v in fl[:4]]} raw={d[:24].hex()}', flush=True)
last = now
time.sleep(0.05)
os.close(mem)
print('render done', flush=True)
proc.wait(timeout=10)
print('done', flush=True)
if __name__ == '__main__':
main()