#!/usr/bin/env python3 import os, glob, sys, time, subprocess REAPER = "/usr/bin/reaper" RPP = "/home/m/soothe-bt/dep0b_rt.rpp" OUTDIR = "/home/m/re-tools/regions_dep" os.makedirs(OUTDIR, exist_ok=True) def find_soothe2_pid(): for p in glob.glob('/proc/[0-9]*'): try: pid = int(os.path.basename(p)) cmd = open(p + '/cmdline','rb').read().replace(b'\0',b' ').decode('utf8','replace') if 'yabridge' not in cmd and 'wine' not in cmd: continue maps = open(f'/proc/{pid}/maps').read() if 'soothe2' in maps: st = open(p + '/stat','rb').read().decode('utf8','replace') state = st.split(')')[-1].split()[0] return pid, state except Exception: pass return None, None def init_ok(pid, base=0x180000000): try: maps = open(f'/proc/{pid}/maps').read() except Exception: return False seen = 0 for line in maps.splitlines(): parts = line.split() if len(parts) < 6: continue lo = int(parts[0].split('-')[0],16) if 'r' in parts[1] and base <= lo < base+0x7000000: seen += 1 return seen >= 8 def dump_module(pid, base=0x180000000, extent=0x7000000): maps = open(f'/proc/{pid}/maps').read() readable = [] for line in maps.splitlines(): parts = line.split() if len(parts) < 6: continue lo, hi = (int(x,16) for x in parts[0].split('-')) if 'r' in parts[1] and (base <= lo < base+extent): readable.append((lo, hi, parts[1])) print(f' pid {pid}: {len(readable)} regions') mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY) total = 0 for lo, hi, perms in readable: try: data = os.pread(mem, hi-lo, lo) total += len(data) fn = os.path.join(OUTDIR, f'pid{pid}_{lo:08x}_{hi:08x}.bin') with open(fn,'wb') as f: f.write(data) except Exception as e: print(' fail', lo, e) os.close(mem) print(f' dumped total {total} bytes in {time.time()-T0:.1f}s') T0 = time.time() print("spawning reaper (realtime render)...") proc = subprocess.Popen( [REAPER, "-nosplash", "-renderproject", RPP], stdout=open('/tmp/harness_dep.log','w'), stderr=subprocess.STDOUT, env={**os.environ}) print("reaper pid", proc.pid) deadline = time.time() + 200 dumped = False while time.time() < deadline and proc.poll() is None: pid, state = find_soothe2_pid() if pid and init_ok(pid): print(f' found pid {pid} state {state}, dumping...') try: dump_module(pid) except Exception as e: print(' dump error:', e) dumped = True break time.sleep(0.05) if not dumped: print("no live soothe2 captured") print("waiting for render to finish...") try: proc.wait(timeout=240) except Exception: proc.kill() print(f"done in {time.time()-T0:.0f}s, exit {proc.returncode}")