diff --git a/AGENTS.md b/AGENTS.md index 0a44ede..6eb7d31 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -125,6 +125,13 @@ err = db(ta(out,1000)/ta(ref,1000)) # err в dB, цель <0.1 **Статус:** chain отключен, используется VLAW path (`RT_VLAW=1`). Target `0.314→<0.05` требует **live-dump chain I/O** или полной транскрипции data flow из decompilation. +**Live-dump chain (ptrace):** chain вызывается в рантайме (DIV#0-7 на `0x1803a06a0`): +- Input: `a` = bands_curve (VLAW output, min=0, max=17.6, mean=0.048) +- `b` = tmp6f8 (step 9b accumulation, min=0, max=0.8, mean=0.8) +- 8 DIV hits на одном кадре (dual-band: 2 bands × 4 iterations?) + +**Для полного live-dump нужно:** breakpoints на всех шагах 9-19 (LOG#1, DIVIDE, dc40, FMA, EXP#1, array-mul, kWarp, LOG#2, IIR4×2, EXP#2). + --- ## Чистая работа diff --git a/scripts/wine_ptrace_trace.py b/scripts/wine_ptrace_trace.py index 97954bf..a635c48 100644 --- a/scripts/wine_ptrace_trace.py +++ b/scripts/wine_ptrace_trace.py @@ -37,6 +37,11 @@ BP_CIN = 0x180529c60 BP_COUT = 0x180529ee1 BP_AIN = 0x180016140 BP_AOUT = 0x18000332c +# Chain 9-19 breakpoints (BLOCKMAP:620-644) +BP_CHAIN_START = 0x18052a580 # band loop start (52a580) +BP_CHAIN_LOG1 = 0x18052a63a # LOG#1 in chain (52a63a) +BP_CHAIN_LOG2 = 0x18052aefd # LOG#2 in chain (52aefd) +BP_CHAIN_END = 0x18052b3cd # chain end / FIR start (52b3cd) CTX_SLOTS = {'scr': 0x540628, 'trk': 0x540688, 'cur': 0x540678, 'fir_ptr': 0x540668} @@ -281,7 +286,12 @@ def main(): ('DIV', BP_DIV), ('DC40', BP_DC40), ('EXPVAR', BP_EXPVAR), ('FN', BP_FN), ('CIN', BP_CIN), ('COUT', BP_COUT), - ('AIN', BP_AIN), ('AOUT', BP_AOUT)): + ('AIN', BP_AIN), ('AOUT', BP_AOUT), + # Chain 9-19 breakpoints + ('CHAIN_START', BP_CHAIN_START), + ('CHAIN_LOG1', BP_CHAIN_LOG1), + ('CHAIN_LOG2', BP_CHAIN_LOG2), + ('CHAIN_END', BP_CHAIN_END)): if not mapped(addr): print('!! %s@%#x не смапплен — пропуск' % (nm_ := name, addr), flush=True) continue @@ -299,9 +309,11 @@ def main(): samples = [] hits = {'COPY': 0, 'EXP': 0, 'DF0': 0, 'DF0RET': 0, 'TRACKSAVE': 0, 'DIV': 0, 'DC40': 0, 'EXPVAR': 0, 'FN': 0, - 'CIN': 0, 'COUT': 0, 'AIN': 0, 'AOUT': 0} + 'CIN': 0, 'COUT': 0, 'AIN': 0, 'AOUT': 0, + 'CHAIN_START': 0, 'CHAIN_LOG1': 0, 'CHAIN_LOG2': 0, 'CHAIN_END': 0} track_by_tid = {} track_dumps = [] + chain_dumps = [] regs_by_tid = {} t_start = time.time() @@ -410,8 +422,35 @@ def main(): continue if kind == 'DF0': track_by_tid[pid] = regs.rdx + # Chain 9-19: dump buffers at key points + if kind in ('CHAIN_START', 'CHAIN_LOG1', 'CHAIN_LOG2', 'CHAIN_END'): + rec_c = {'kind': kind, 'tid': pid, + 't': round(time.time()-t_start, 4), + 'rcx': regs.rcx, 'rdx': regs.rdx, + 'r8': regs.r8, 'r9': regs.r9, + 'rsp': regs.rsp} + try: + # chain input: [678i] = rcx (bands_curve), [6f8] = rdx (tmp6f8) + if regs.rcx > 0x10000 and regs.rdx > 0x10000: + rec_c['bands_in'] = rd_f32(regs.rcx, min(2050, 512)) # first 512 vals + rec_c['tmp6f8_in'] = rd_f32(regs.rdx, min(2050, 512)) + rec_c['acc_ptr'] = regs.r8 if regs.r8 > 0x10000 else 0 + if rec_c['acc_ptr']: + rec_c['acc_in'] = rd_f32(rec_c['acc_ptr'], min(2050, 512)) + except OSError as e: + rec_c['err'] = str(e) + chain_dumps.append(rec_c) + hits[kind] += 1 + # standard int3 handling + poke(pid, site, (peek(pid, site) & ~0xFF) | obyte) + regs.rip = site + setregs(pid, regs) + pt(PTRACE_SINGLESTEP, pid, 0, 0) + os.waitpid(pid, __WALL) + poke(pid, site, (peek(pid, site) & ~0xFF) | 0xCC) + pt(PTRACE_CONT, pid, 0, 0) + continue if kind in ('CIN','COUT'): - key='cin_%d'%pid if kind=='CIN' else 'cout_%d'%pid if kind=='CIN': regs_by_tid[pid]=dict(rdx=regs.rdx,r12=regs.r12, rcx=regs.rcx) @@ -632,7 +671,7 @@ def main(): snap_ptrs[nm] = p snap_arr[nm] = rd_f32(p, 4100) with open(os.path.join(outdir, 'chain_samples.pkl'), 'wb') as f: - pickle.dump({'samples': samples, 'snap_ptrs': snap_ptrs, 'ctx': ctx}, f) + pickle.dump({'samples': samples, 'chain_dumps': chain_dumps, 'snap_ptrs': snap_ptrs, 'ctx': ctx}, f) np.savez_compressed(os.path.join(outdir, 'ctx_snap.npz'), **snap_arr) print('saved %d -> %s' % (len(samples), outdir), flush=True) for _ in range(600):