mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
perf: read_ram 16KB cap + 4KB divergence chunks + send_fmt 64KB buffer
- debug_server.c: increase read_ram from 256 bytes to 16KB max, use dynamic allocation for hex buffer; increase send_fmt buffer from 16KB to 64KB to hold large responses - find_divergence.py: use 4KB chunks (was 256), add total diff byte count, remove run-length cap, remove hardcoded VSync exclusion, add data length verification on reads 512KB paused scan: 128s (was 1393s with 256-byte chunks) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -268,7 +268,7 @@ void debug_server_send_line(const char *json)
|
||||
|
||||
void debug_server_send_fmt(const char *fmt, ...)
|
||||
{
|
||||
char buf[16384];
|
||||
char buf[65536];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
@@ -346,14 +346,16 @@ static void handle_read_ram(int id, const char *json)
|
||||
uint32_t addr = hex_to_u32(addr_str);
|
||||
int len = json_get_int(json, "len", 1);
|
||||
if (len < 1) len = 1;
|
||||
if (len > 256) len = 256;
|
||||
if (len > 16384) len = 16384; /* 16KB max */
|
||||
|
||||
char hex[513];
|
||||
char *hex = (char *)malloc(len * 2 + 1);
|
||||
if (!hex) { send_err(id, "alloc failed"); return; }
|
||||
for (int i = 0; i < len; i++)
|
||||
snprintf(hex + i * 2, 3, "%02x", psx_read_byte(addr + i));
|
||||
|
||||
send_fmt("{\"id\":%d,\"ok\":true,\"addr\":\"0x%08X\",\"len\":%d,\"hex\":\"%s\"}",
|
||||
id, addr, len, hex);
|
||||
free(hex);
|
||||
}
|
||||
|
||||
static void handle_dump_ram(int id, const char *json)
|
||||
|
||||
+42
-26
@@ -68,20 +68,25 @@ class DebugClient:
|
||||
time.sleep(0.1)
|
||||
|
||||
def read_ram(self, addr, length):
|
||||
"""Read up to 256 bytes."""
|
||||
"""Read up to 16KB."""
|
||||
resp = self.send({"cmd": "read_ram", "addr": hex(addr), "len": length})
|
||||
if not resp.get('ok'):
|
||||
raise RuntimeError(f"read_ram failed at 0x{addr:08X}: {resp}")
|
||||
return bytes.fromhex(resp['hex'])
|
||||
data = bytes.fromhex(resp['hex'])
|
||||
if len(data) != length:
|
||||
raise RuntimeError(f"read_ram at 0x{addr:08X}: requested {length}, got {len(data)}")
|
||||
return data
|
||||
|
||||
|
||||
CHUNK_SIZE = 4096 # 4KB per request — fits in both servers' buffers
|
||||
|
||||
def read_ram_range(client, lo, hi, label=""):
|
||||
"""Read a full RAM range in 256-byte chunks using a persistent connection."""
|
||||
"""Read a full RAM range in CHUNK_SIZE-byte chunks using a persistent connection."""
|
||||
result = bytearray()
|
||||
total = hi - lo
|
||||
done = 0
|
||||
for addr in range(lo, hi, 256):
|
||||
chunk_len = min(256, hi - addr)
|
||||
for addr in range(lo, hi, CHUNK_SIZE):
|
||||
chunk_len = min(CHUNK_SIZE, hi - addr)
|
||||
chunk = client.read_ram(addr, chunk_len)
|
||||
result.extend(chunk)
|
||||
done += chunk_len
|
||||
@@ -93,8 +98,13 @@ def read_ram_range(client, lo, hi, label=""):
|
||||
|
||||
|
||||
def find_divergences(recomp_data, oracle_data, base_addr, excludes, max_results=20):
|
||||
"""Compare two RAM buffers and return list of divergences."""
|
||||
"""Compare two RAM buffers and return (divergences_list, total_diff_bytes, total_compared).
|
||||
|
||||
Never silently truncates: always returns full counts even when
|
||||
max_results limits the detailed list."""
|
||||
divergences = []
|
||||
total_diff_bytes = 0
|
||||
total_compared = 0
|
||||
i = 0
|
||||
while i < len(recomp_data) and i < len(oracle_data):
|
||||
addr = base_addr + i
|
||||
@@ -102,33 +112,36 @@ def find_divergences(recomp_data, oracle_data, base_addr, excludes, max_results=
|
||||
excluded = False
|
||||
for ex_addr, ex_len in excludes:
|
||||
if ex_addr <= addr < ex_addr + ex_len:
|
||||
i = ex_addr + ex_len - base_addr # skip past exclusion
|
||||
i = ex_addr + ex_len - base_addr
|
||||
excluded = True
|
||||
break
|
||||
if excluded:
|
||||
continue
|
||||
|
||||
total_compared += 1
|
||||
if recomp_data[i] != oracle_data[i]:
|
||||
# Found a divergence — collect the full divergent run
|
||||
total_diff_bytes += 1
|
||||
# Collect the full divergent run (no length cap)
|
||||
run_start = i
|
||||
while (i < len(recomp_data) and i < len(oracle_data) and
|
||||
recomp_data[i] != oracle_data[i] and
|
||||
i - run_start < 64):
|
||||
recomp_data[i] != oracle_data[i]):
|
||||
if i > run_start:
|
||||
total_compared += 1
|
||||
total_diff_bytes += 1
|
||||
i += 1
|
||||
run_end = i
|
||||
|
||||
divergences.append({
|
||||
'addr': base_addr + run_start,
|
||||
'len': run_end - run_start,
|
||||
'recomp': recomp_data[run_start:run_end],
|
||||
'oracle': oracle_data[run_start:run_end],
|
||||
})
|
||||
if len(divergences) >= max_results:
|
||||
break
|
||||
if len(divergences) < max_results:
|
||||
divergences.append({
|
||||
'addr': base_addr + run_start,
|
||||
'len': run_end - run_start,
|
||||
'recomp': recomp_data[run_start:run_end],
|
||||
'oracle': oracle_data[run_start:run_end],
|
||||
})
|
||||
else:
|
||||
i += 1
|
||||
|
||||
return divergences
|
||||
return divergences, total_diff_bytes, total_compared
|
||||
|
||||
|
||||
def format_divergence(d):
|
||||
@@ -169,10 +182,9 @@ def main():
|
||||
help='Skip pause/continue (read live)')
|
||||
args = parser.parse_args()
|
||||
|
||||
# Parse exclusions
|
||||
# Parse exclusions — nothing excluded by default.
|
||||
# Use --exclude 0x80079D9C:4 to exclude VSync counter if needed.
|
||||
excludes = []
|
||||
# Always exclude VSync counter (timing-dependent)
|
||||
excludes.append((0x80079D9C, 4))
|
||||
for ex in args.exclude:
|
||||
parts = ex.split(':')
|
||||
ex_addr = int(parts[0], 0)
|
||||
@@ -244,14 +256,18 @@ def main():
|
||||
oracle.close()
|
||||
|
||||
# Find divergences
|
||||
divs = find_divergences(recomp_ram, oracle_ram, args.lo, excludes, args.max)
|
||||
divs, total_diff, total_compared = find_divergences(
|
||||
recomp_ram, oracle_ram, args.lo, excludes, args.max)
|
||||
|
||||
print(f"SUMMARY: {total_diff} bytes differ out of {total_compared} compared "
|
||||
f"({total_diff*100/max(total_compared,1):.2f}%)")
|
||||
print(f" Showing {len(divs)} divergent region(s)" +
|
||||
(f" (capped at --max {args.max}; there may be more)" if len(divs) >= args.max else ""))
|
||||
print()
|
||||
|
||||
if not divs:
|
||||
print("NO DIVERGENCES FOUND in scanned range.")
|
||||
print("(This may mean both sides match, or the relevant state is outside this range.)")
|
||||
else:
|
||||
print(f"FOUND {len(divs)} DIVERGENCE(S):")
|
||||
print()
|
||||
for i, d in enumerate(divs):
|
||||
print(f"#{i+1}: {format_divergence(d)}")
|
||||
print()
|
||||
|
||||
Reference in New Issue
Block a user