From ab9c5823eecc988fa9bb1cc0fe1a6a08dd6e9e86 Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Wed, 22 Jul 2026 23:11:02 -0400 Subject: [PATCH] tcg/wasm32: add opt-in ramrace guest-RAM/TLB probe ?ramrace logs off-vCPU guest DRAM reads/writes/maps and TLB flushes. The open-window crash shows zero such accesses, ruling out IO-thread guest-RAM access during inflate as the compiled-store corruption source. --- accel/tcg/cputlb.c | 29 +++++++++++++++++ system/physmem.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index a673eb2eb7..7fc3fd0b8b 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -73,6 +73,28 @@ } \ } while (0) +#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) +extern int wasm_ramrace_trace; +static uint32_t wasm_ramrace_tlb_logs; + +static void wasm_ramrace_log_tlb(const char *kind, CPUState *cpu, vaddr addr) +{ + if (!wasm_ramrace_trace || current_cpu) { + return; + } + + wasm_ramrace_tlb_logs++; + if (wasm_ramrace_tlb_logs <= 200 || (wasm_ramrace_tlb_logs % 10000) == 0) { + printf("RAMRACE tlb-%s off-vcpu #%u tid=%lu cpu=%p addr=0x%llx\n", + kind, wasm_ramrace_tlb_logs, (unsigned long)pthread_self(), + cpu, (unsigned long long)addr); + fflush(stdout); + } +} +#else +#define wasm_ramrace_log_tlb(kind, cpu, addr) ((void)0) +#endif + #define assert_cpu_is_self(cpu) do { \ if (DEBUG_TLB_GATE) { \ g_assert(!(cpu)->created || qemu_cpu_is_self(cpu)); \ @@ -417,6 +439,7 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) { tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap); + wasm_ramrace_log_tlb("full", cpu, 0); assert_cpu_is_self(cpu); @@ -433,6 +456,7 @@ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap) const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; tlb_debug("mmu_idx: 0x%"PRIx16"\n", idxmap); + wasm_ramrace_log_tlb("full-all", src_cpu, 0); flush_all_helper(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); @@ -608,6 +632,7 @@ static void tlb_flush_page_by_mmuidx_async_2(CPUState *cpu, void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap) { tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%" PRIx16 "\n", addr, idxmap); + wasm_ramrace_log_tlb("page", cpu, addr); assert_cpu_is_self(cpu); @@ -627,6 +652,7 @@ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap) { tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap); + wasm_ramrace_log_tlb("page-all", src_cpu, addr); /* This should already be page aligned */ addr &= TARGET_PAGE_MASK; @@ -777,6 +803,7 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, { TLBFlushRangeData d; + wasm_ramrace_log_tlb("range", cpu, addr); assert_cpu_is_self(cpu); /* @@ -817,6 +844,8 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu, TLBFlushRangeData d, *p; CPUState *dst_cpu; + wasm_ramrace_log_tlb("range-all", src_cpu, addr); + /* * If all bits are significant, and len is small, * this devolves to tlb_flush_page. diff --git a/system/physmem.c b/system/physmem.c index addd5d0c4b..937ac4d663 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -78,6 +78,10 @@ #include "monitor/monitor.h" +#ifdef EMSCRIPTEN +#include +#endif + #ifdef CONFIG_LIBDAXCTL #include #endif @@ -89,6 +93,47 @@ */ RAMList ram_list = { .blocks = QLIST_HEAD_INITIALIZER(ram_list.blocks) }; +#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) +int wasm_ramrace_trace; +static uint32_t wasm_ramrace_read_logs; +static uint32_t wasm_ramrace_cached_read_logs; +static uint32_t wasm_ramrace_write_logs; +static uint32_t wasm_ramrace_cached_write_logs; +static uint32_t wasm_ramrace_map_logs; +static uint32_t wasm_ramrace_unmap_logs; + +EMSCRIPTEN_KEEPALIVE void wasm_enable_ramrace_trace(void) +{ + wasm_ramrace_trace = 1; +} + +static bool wasm_ramrace_overlaps_dram(hwaddr addr, hwaddr len) +{ + const hwaddr dram_lo = 0x3fc80000u; + const hwaddr dram_hi = 0x3fce0000u; + + return len > 0 && addr < dram_hi && addr + len > dram_lo; +} + +static void wasm_ramrace_log_access(const char *kind, hwaddr addr, hwaddr len, + uint32_t *counter) +{ + if (!wasm_ramrace_trace || current_cpu || !wasm_ramrace_overlaps_dram(addr, len)) { + return; + } + + (*counter)++; + if (*counter <= 200 || (*counter % 10000) == 0) { + printf("RAMRACE %s off-vcpu #%u tid=%lu addr=0x%llx len=0x%llx\n", + kind, *counter, (unsigned long)pthread_self(), + (unsigned long long)addr, (unsigned long long)len); + fflush(stdout); + } +} +#else +#define wasm_ramrace_log_access(kind, addr, len, counter) ((void)0) +#endif + static MemoryRegion *system_memory; static MemoryRegion *system_io; @@ -2948,6 +2993,9 @@ MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, FlatView *fv; if (len > 0) { +#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) + wasm_ramrace_log_access("address_space_read", addr, len, &wasm_ramrace_read_logs); +#endif RCU_READ_LOCK_GUARD(); fv = address_space_to_flatview(as); result = flatview_read(fv, addr, attrs, buf, len); @@ -2964,6 +3012,9 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, FlatView *fv; if (len > 0) { +#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) + wasm_ramrace_log_access("address_space_write", addr, len, &wasm_ramrace_write_logs); +#endif RCU_READ_LOCK_GUARD(); fv = address_space_to_flatview(as); result = flatview_write(fv, addr, attrs, buf, len); @@ -3239,6 +3290,15 @@ void *address_space_map(AddressSpace *as, FlatView *fv; trace_address_space_map(as, addr, len, is_write, *(uint32_t *) &attrs); +#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) + if (is_write) { + wasm_ramrace_log_access("address_space_map_write", addr, len, + &wasm_ramrace_map_logs); + } else { + wasm_ramrace_log_access("address_space_map_read", addr, len, + &wasm_ramrace_map_logs); + } +#endif if (len == 0) { return NULL; @@ -3304,6 +3364,18 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, mr = memory_region_from_host(buffer, &addr1); if (mr != NULL) { if (is_write) { +#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) + if (wasm_ramrace_trace && !current_cpu && access_len > 0) { + wasm_ramrace_unmap_logs++; + if (wasm_ramrace_unmap_logs <= 200 || + (wasm_ramrace_unmap_logs % 10000) == 0) { + printf("RAMRACE address_space_unmap_write off-vcpu #%u tid=%lu host-ram=0x%llx len=0x%llx\n", + wasm_ramrace_unmap_logs, (unsigned long)pthread_self(), + (unsigned long long)addr1, (unsigned long long)access_len); + fflush(stdout); + } + } +#endif invalidate_and_set_dirty(mr, addr1, access_len); } if (xen_enabled()) { @@ -3519,6 +3591,10 @@ address_space_read_cached_slow(MemoryRegionCache *cache, hwaddr addr, hwaddr mr_addr, l; MemoryRegion *mr; +#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) + wasm_ramrace_log_access("address_space_read_cached", addr, len, + &wasm_ramrace_cached_read_logs); +#endif l = len; mr = address_space_translate_cached(cache, addr, &mr_addr, &l, false, MEMTXATTRS_UNSPECIFIED); @@ -3536,6 +3612,10 @@ address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, hwaddr mr_addr, l; MemoryRegion *mr; +#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) + wasm_ramrace_log_access("address_space_write_cached", addr, len, + &wasm_ramrace_cached_write_logs); +#endif l = len; mr = address_space_translate_cached(cache, addr, &mr_addr, &l, true, MEMTXATTRS_UNSPECIFIED);