Diagnose data-corruption failures in tt-emule operations (wrong values, partial zeros, off-by-N row/col writes). Use when a regression test reports ATOL > 1e-3 on otherwise-functional kernel paths.
Scanned 9/11/2026
Install to Claude Code
npx -y skills add tenstorrent/tt-emule --skill memory-debug --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Memory Debug?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/tenstorrent-memory-debug)More formats (shields.io, HTML) on the badges page.
---
name: memory-debug
description: Diagnose data-corruption failures in tt-emule operations (wrong values, partial zeros, off-by-N row/col writes). Use when a regression test reports ATOL > 1e-3 on otherwise-functional kernel paths.
---
# Memory & data-corruption debug for tt-emule
Distills the diagnostic playbook used across rounds 7-12. Targets the
failure mode "kernel runs, no crash, but output bytes don't match
input". Most fixes land in `include/jit_hw/api/dataflow/`,
`include/jit_hw/llk_pack.h`, `include/jit_hw/api/compute/common.h`,
or `tt_metal/impl/emulation/emulated_program_runner.cpp`.
For pure JIT-compile failures (missing shim), use `/compute-llk-bringup`.
For missing API surface, use `/implement-mock`.
## When to invoke
- "ATOL > 1e-3" / "PCC failure" / "max diff X" on a kernel that
previously worked or that runs to completion.
- "Output is mostly correct but has wrong regions" / "some rows are
wrong" / "some tiles are zero".
- "Sharded → interleaved" (or vice versa) data movement is wrong.
- "Output bytes are zero" where they shouldn't be.
Also applies when:
- Test crashes / segfault / abort during kernel execution (not at
compile/dispatch) — segfaults are usually one specific
bad-pointer-dereference reachable through the same trace pass plus
a gdb post-mortem (see "Crashes" section below).
Don't invoke for:
- "Test fails to compile" — that's `/compute-llk-bringup`.
## The discriminator: zeros vs random-wrong
The very first question to answer:
| Output at the wrong cells | What it means |
|---|---|
| **Exact `0.0`** (bf16: `0x0000`, fp32: `0x00000000`) | L1 was never written there. Bug is "writer skipped" or "writer's dest_addr is wrong". |
| Non-zero but mismatched | Writer wrote *something* — either wrong src bytes or wrong dst location with another shard's data. |
| All zeros across the entire region | Entire NOC write missed. Bank topology, dst_addr crashed silently, or bank_offset_idx out of range. |
| Off-by-N pattern (e.g., row N→row N+1) | Address arithmetic off by a stride; trace `output_page_id`. |
This single bit of evidence narrows from "anywhere in pipeline" to
"compute" vs "writer" vs "address resolution".
L1 zero-init is in `Core::l1_ptr` allocation in
`tt_metal/impl/emulation/`. If output bytes are exact zero, no
NOC write landed there — assume "writer skipped" until proven otherwise.
## Layer chain (where to look)
```
torch input
↓
ttnn.from_torch / to_device ← host I/O
↓
SWEmuleChip::write_to_device ← emule host→L1 memcpy (W2D layer)
↓
input L1 buffer ← emule: Core::l1_ptr
↓ ← anything between W2D and the kernel
that touches L1: reset_l1_bump,
init_cb_sync, scratch memsets, etc.
↓
reader kernel: cb_reserve_back / noc_async_read
↓
input CB (per-core) ← __emule_compute::cb_read/write_ptr
↓
compute kernel: copy_tile / __llk_pack_untilize / sfpu ops
↓
DST array (__emule_dst[16][1024]) ← per-thread, float32
↓
output CB (per-core)
↓
writer kernel: cb_wait_front / noc_async_write
↓
output L1 buffer (interleaved or sharded)
↓
ttnn.to_torch
```
Trace at each ↓. The first transition where bytes diverge from
expected is the fault site. Pay particular attention to the gap
between W2D and the kernel — that's where host-side scratch /
reset hooks can silently mutate L1 between when the host wrote
real data and when the kernel reads it.
## Diagnostic recipe — the 5-step trace pass
### Step 1: capture the corruption pattern in python
Stand up a canonical repro `.py` outside the pytest harness — pytest
runs in parallel and burns the shared JIT cache (`/tmp/tt_emule_jit_cache_$UID`).
A 30-line script that calls `ttnn.from_torch → to_device → op → to_torch`
and prints `diff` arrays is faster to iterate than pytest.
Then characterize:
```python
diff = (np.abs(inp_flat - out_flat) > 1e-3)
print(f"wrong elements: {diff.sum()} / {diff.size}")
print(f"rows with any wrong cell: {np.flatnonzero(diff.any(axis=1)).tolist()}")
# Strides between wrong rows tell you tile/face boundaries
print(f"strides: {np.diff(np.flatnonzero(diff.any(axis=1))).tolist()}")
# Are the wrong columns identical across all wrong rows?
unique_col_sets = {tuple(np.flatnonzero(diff[r]).tolist()) for r in np.flatnonzero(diff.any(axis=1))}
print(f"unique col-sets: {len(unique_col_sets)}")
# What's at the wrong cells — exact zero or random wrong?
nz_at_wrong = (out_flat[diff] != 0.0).sum()
print(f"of {diff.sum()} wrong cells, {nz_at_wrong} are non-zero")
```
Strides of 16 or 32 → tile/face boundary. Strides of 32×tile_height
→ tile-row boundary. Repeating-mod stride at shard-height intervals
(1024, 256, …) → bug is per-shard at the same offset.
### Step 2: locate the cell in tile-coords
Map the corrupted (row, col) back to:
- Tile-row index: `row / 32`
- Sub-row within tile: `row % 32`
- Tile-col index: `col / 32`
- Sub-col within tile: `col % 32`
- Face index: `(sub_row >= 16) * 2 + (sub_col >= 16)` → 0/1/2/3
- DST index: `r * 32 + c` for `r = sub_row, c = sub_col`
- `rowmajor_to_nfaces[r*32+c] = face*256 + (r%16)*16 + (c%16)`
### Step 3: instrument the suspected hop with gated fprintf
Pattern (gate by env var **and** the specific tile/offset of interest
to avoid 4096-line dumps):
```cpp
// in noc_async_write — writer side, addr resolution
if (getenv("TT_EMULE_TRACE_WRITE")) {
const uint16_t* sp = reinterpret_cast<const uint16_t*>(src);
fprintf(stderr, "[WRITE] src_l1=0x%x → dst_noc=0x%llx size=%u "
"first8=%04x %04x %04x %04x %04x %04x %04x %04x\n",
(unsigned)src_local_l1_addr, (unsigned long long)dst_noc_addr,
(unsigned)size, sp[0], sp[1], sp[2], sp[3],
sp[4], sp[5], sp[6], sp[7]);
}
```
```cpp
// in __llk_pack_untilize — compute output → CB
bool trace_this = getenv("TT_EMULE_TRACE_PACK") && tile_idx == K && __llk_pack_offset == O;
if (trace_this) {
fprintf(stderr, "[PACK_PRE] DST[%u] face%u first8: %.4f %.4f %.4f %.4f ...\n",
tile_idx, face, __emule_dst[tile_idx][r*32+c0], ...);
}
// ... do the pack ...
if (trace_this) {
const uint16_t* rb = reinterpret_cast<const uint16_t*>(base + cb_byte_offset);
fprintf(stderr, "[PACK_POST] CB bytes %u..%u: %04x %04x ... \n",
cb_byte_offset, cb_byte_offset+31, rb[0], rb[1], ...);
}
```
```cpp
// in __emule_unpack_cb_tile_to (common.h) — CB → DST
if (getenv("TT_EMULE_TRACE_UNPACK") && tile_idx == K) {
fprintf(stderr, "[UNPACK] src_bf16[528]=%04x out[528]=%.4f ...\n",
bf[__emule_nfaces::rowmajor_to_nfaces[528]], out[528]);
}
```
Always gate by **at least** one specific position (tile_idx, offset,
`output_page_id == X`) — emule generates O(num_tiles × num_pages)
log lines without a filter.
**When the failure only occurs at iteration N (not iteration 0)** —
your `tile_idx == K` gate fires once per block × num_cores, so the
first samples you print are block 0, block 1, etc. — not the
failing block. You'll see "looks correct" and miss the bug. Fix:
add a thread-local block counter that increments when the kernel
starts a new block (e.g. `if (tile_idx == 0 && __llk_pack_offset == 0)
__block_counter++;`) and gate trace on `__block_counter == N`.
Counter is `thread_local` so each core has its own — block 18 on
core 0 and block 18 on core 1 both print, confirming
per-shard-reproducibility (or surfacing a single-shard outlier).
**When you've gated on a specific tile and the bytes look wrong** —
the next question is "is this tile actually special, or is everything
in this block wrong?" Add a scan that walks ALL tiles in the failing
iteration (loop `t = 0..num_tiles_per_block` over
`cb_read_ptr_at(icb, t)`) and prints non-zero counts per tile. If
only one tile shows the anomaly, the bug is tile-position-specific.
If all tiles show it, the bug is block-level.
**Host-side trace (W2D layer)** — when the kernel reads zeros at
bytes you believe the host wrote, instrument
`SWEmuleChip::write_to_device` in
`tt_metal/third_party/umd/device/chip/sw_emule_chip.cpp` to log
`(core, l1_dest, size, first/last bytes)`. Run with the trace and
compute the absolute L1 offset of the corrupted bytes (CB virtual
ptr − core mmap base + intra-tile offset). Cross-reference: did the
host actually write non-zero bytes at that offset? If yes, something
between W2D completion and kernel read mutated L1 — audit anything
in `tt_emule/` that touches L1 between programs (reset hooks,
scratch memsets, init routines for mailbox / barrier / debug
regions). The relevant offsets are usually fixed constants whose
value was correct under an older L1 layout but now overlaps
allocator output.
### Step 4: rebuild + rerun the canonical repro
```bash
rm -rf /tmp/tt_emule_jit_cache_$(id -u) # one-shot per instrumentation edit
cd /localdev/arminale/tt-metal && \
PYTHONPATH=$PWD/ttnn:$PWD/tools:$PWD/build_emule/lib:$PWD \
LD_LIBRARY_PATH=$PWD/build_emule/lib \
TT_METAL_HOME=$PWD TT_METAL_RUNTIME_ROOT=$PWD \
TT_METAL_EMULE_MODE=1 TT_METAL_SLOW_DISPATCH_MODE=1 \
TT_METAL_MOCK_CLUSTER_DESC_PATH=$PWD/tt_metal/third_party/umd/tests/cluster_descriptor_examples/wormhole_N150.yaml \
MESH_DEVICE=N150 \
TT_EMULE_TRACE_PACK=1 \
timeout 90 /opt/ttmlir-toolchain/venv/bin/python /tmp/your_repro.py 2>&1 | tee /tmp/round.log
```
Critical: JIT cache invalidation per instrumentation edit. `clang -E`
caches the preprocessed kernel — a fprintf added without bumping the
cache will be silently elided.
### Step 5: discriminate
| Trace evidence | Diagnosis | Where to fix |
|---|---|---|
| PACK_PRE shows correct DST, PACK_POST shows correct CB bytes, dest L1 has zeros | Writer never wrote those bytes (or wrote elsewhere) | `noc_async_write` resolution, `s.get_noc_addr`, or writer kernel logic |
| PACK_PRE shows correct DST, PACK_POST shows zeros in CB | `__llk_pack_untilize` row/col arithmetic bug | `llk_pack.h::__llk_pack_untilize` row_stride / tile_row_offset / tile_col_offset |
| PACK_PRE shows zeros in DST | `copy_tile` or `__emule_unpack_cb_tile_to` doesn't fill DST face | `common.h::copy_tile`, `__emule_unpack_cb_tile_to` |
| WRITE trace shows `src_ptr` first8 = zeros | Writer reading from wrong CB offset, or compute didn't write CB before writer's `cb_wait_front` returned | CB sync (cb_push_back / cb_wait_front semantics) |
| WRITE trace shows correct src bytes but `dst_noc_addr` resolves to wrong core | Bank topology bug | `emulated_program_runner.cpp::populate_bank_mapping` or `dataflow_api_addrgen.h` extern arrays |
| WRITE trace shows duplicate writes from two shards to the same `dst_noc_addr` | Cross-shard race / host-side address compute wrong | Trace `runtime_args` of writer kernel, check if `width_wise_output_block_start_index` is double-allocated |
## Common root-cause classes (sorted by hit count across rounds 7-12)
### Class 1: bank topology not populated from SoC descriptor
**Symptoms**: `Core::l1_ptr OOB` crashes with very high offsets
(`0x7fc5750`, `0x834b750`); or large blocks of output are exact zero;
or "all writes to bank N fail".
**Root cause**: `emulated_program_runner.cpp` ships a placeholder
`EMULE_NUM_L1_BANKS = 1` or `EMULE_NUM_DRAM_BANKS = N` constant that
doesn't match the real `device->allocator()->get_num_banks(...)`.
**Fix shape**: populate from the allocator in `populate_bank_mapping`:
```cpp
const auto& allocator = device->allocator();
num_banks_out = allocator->get_num_banks(BufferType::L1); // or DRAM
for (uint32_t b = 0; b < num_banks_out && b < MAX_NUM_BANKS; ++b) {
auto logical = allocator->get_logical_core_from_bank_id(b);
auto virt = device->virtual_core_from_logical_core(logical, CoreType::WORKER);
uint16_t noc_xy = (uint16_t(virt.y) << NOC_NODE_ID_BITS) | uint16_t(virt.x);
bank_to_noc_xy[0][b] = noc_xy;
bank_to_noc_xy[1][b] = noc_xy;
}
```
Thread `num_banks` through `populate_bank_mapping` →
`execute_program_emulated` → `collect_kernels` →
`build_kernel_defines` and emit `NUM_L1_BANKS=N` /
`LOG_BASE_2_OF_NUM_L1_BANKS` / `IS_NOT_POW2_NUM_L1_BANKS`. Match the
extern array sizes in `dataflow_api_addrgen.h` /
`jit_kernel_stubs.hpp` (use `[NUM_*_BANKS]`, not literal `[32]`).
Past closures: R7 DRAM (`IS_NOT_POW2_NUM_DRAM_BANKS`), R11 L1 (B8.4
+ B8.1 as side effect).
### Class 2: per-shard same-offset corruption (compute/writer math)
**Symptoms**: All shards corrupt at the *same intra-shard offset*.
With S shards and shard height H, you see corruption at rows
`H·s + k…H·s + k+15` for `s = 0..S-1` and some fixed `k`.
**Diagnosis**: bug is in per-core writer or compute math (the same
math runs on each core, hitting the same threshold).
**Common sub-causes**:
- DST array indexing bug at a specific tile-row threshold
- `__llk_pack_untilize`'s `tile_row` / `tile_col` derived from
`__llk_pack_offset` is wrong above some block_c
- Reader-side `cb_reserve_back` / `cb_push_back` count mismatch
causes compute to read stale CB front
### Class 3: face-3 (bottom-right quadrant) data lost
**Symptoms**: Corrupted region is exactly 16 cols × 16 rows = 256
elements. Position aligned to a tile boundary (col % 32 == 16 and
row % 32 == 16 within the tile).
**Diagnosis**: `rowmajor_to_nfaces` LUT or `copy_tile` / SFPU
`<op>_tile` ops only fill the top-left quadrant of DST.
**Check**: `include/jit_hw/api/compute/nfaces.h::rowmajor_to_nfaces`
should be `face*256 + (r%16)*16 + (c%16)`. Many SFPU `_tile` shims
loop `i in [0, 256)` covering only one face — should be `[0, 1024)`
covering all four.
### Class 4: DAZ / FTZ flushing denormals
**Symptoms**: Output is non-zero but very small input values become
exact zero. Only triggers on `bfloat16 → fp32 → bfloat16` round-trips
where the bf16's exponent is below the fp32 normal range.
**Diagnosis**: SSE control word has DAZ / FTZ enabled in the JIT TU.
**Check**: `_MM_GET_DENORMALS_ZERO_MODE()` and `_MM_GET_FLUSH_ZERO_MODE()`
at the start of the kernel. Random Gaussian inputs in `[-3, 3]` won't
trigger this; tests with handcrafted small inputs will.
### Class 5: extern array size mismatch
**Symptoms**: kernel C++ compiles but linker emits "conflicting
declaration" or runtime accesses go to bank N+offset with stale data.
**Diagnosis**: `bank_to_noc_xy[]` in
`include/jit_hw/internal/dataflow/dataflow_api_addrgen.h` vs
`include/jit_hw/jit_kernel_stubs.hpp` declared with different array
sizes (`[32]` vs `[NUM_L1_BANKS]` vs `[256]`).
**Fix**: align all extern declarations to `[NUM_*_BANKS]` (the JIT
define), matching upstream `dataflow_api_common.h`.
## Crashes (segfault / abort during kernel execution)
When the kernel runs but the process dies mid-execution (SIGSEGV,
abort), the standard 5-step trace doesn't apply directly because
nothing reaches `to_torch`. The data is in registers and the stack
at the moment of the crash. Use gdb post-mortem to pinpoint the
faulting instruction, then re-enter the trace methodology from
the layer just above the fault.
**Localize the fault**:
```bash
# Run the canonical repro under gdb, catching the fatal signal:
gdb -batch \
-ex 'set pagination off' \
-ex 'handle SIGSEGV stop print' \
-ex 'run' \
-ex 'info registers' \
-ex 'x/30i $pc-60' \
--args /opt/.../python /tmp/your_repro.py
```
What you're looking for in the output:
- The PC at the time of the crash. If it lives in a JIT-built `.so`
under `/tmp/tt_emule_jit_cache_*/`, the fault is in a JIT kernel,
not the emule runtime.
- The instruction at the PC. A pattern like `mov (%rdx,%rax,4),%eax`
with a bogus `rdx` value points to an array-of-pointers load
through a truncated/uninitialized pointer.
- Pointer-value sanity: emule maps L1 below 4 GB (MAP_32BIT) and
static globals live high (around `0x7fff…`). A register holding
`0xc0…` or another low 4-byte value is usually a 64-bit pointer
that got narrowed to 32-bit somewhere upstream.
**Keep the JIT build sources for diff**:
The runner cleans up the per-kernel build directory by default.
Gate that cleanup behind an env var (it's typically a single
`std::filesystem::remove_all(dir)` call in
`emulated_program_runner.cpp`) so you can read the generated
`wrapper.cpp` + `patched_kernel.cpp` of the crashing kernel.
Identify the kernel by matching the `.so.tmp.*` path from the gdb
output to its sibling build dir.
**Cross-reference the upstream call**:
Once you know the JIT kernel and the failing instruction, look at
the original kernel `.cpp` and follow the call chain backwards.
Pay particular attention to **silicon-side functions that take or
produce `uint32_t` for an L1 address**. Under emule's L1 offset model
those `uint32_t` values are **0-based firmware offsets**; the deref
site must rebase onto this fiber's `bridge_l1` via
`__emule_local_l1_to_ptr`, which the JIT patch pass inserts (see
`docs/jit-l1-patch-pass.md`).
The failure mode is a **missed deref site**: a 0-based offset (e.g.
~`0x1240`) dereferenced directly as a pointer hits an unmapped low page
→ immediate SIGSEGV at the real `kernel.cpp:<line>` (the intentional
SIGSEGV net). An un-migrated absolute/aliased value that reaches the
translate chokepoint instead trips the debug assert
`[EMULE_L1] L1 offset 0x… out of range`.
The fix is emule-side and lives in the **patch pass**, not the mmap:
add a targeted rewrite rule for the missed idiom so the offset is
rebased at deref (`docs/jit-l1-patch-pass.md` enumerates the rules).
Do **not** reach for `MAP_32BIT` — worker L1 is a plain 64-bit mmap
now, and the old truncated-host-pointer model is retired.
## Dataflow-only debug (no compute kernel involved)
Some ops are pure data movement — permute, sharded↔interleaved
without dtype conversion, transpose. They have a reader and a
writer kernel, no compute. The 5-step trace's compute hop
(DST/PACK) doesn't exist; the pipeline is shorter:
```
input L1 → reader → CB → writer (noc_async_write) → output L1
```
For these, instrument noc_async_write (and noc_async_read for the
reader side). Print `(src_phys_core, src_local_l1_addr,
dst_noc_addr, size)` per write, then in a Python post-pass
decompose `dst_noc_addr`:
```python
# dst_noc_addr layout on WH: [noc_xy:12 << 36] | [l1_offset:36]
import re, collections
nocs, phys, pairs = collections.Counter(), collections.Counter(), collections.defaultdict(set)
for line in open('writes.log'):
m = re.match(r'\[W\] phys=\((\d+),(\d+)\) .* dst_noc=0x([0-9a-f]+) size=(\d+)', line)
if not m: continue
px, py = int(m.group(1)), int(m.group(2))
dst = int(m.group(3), 16)
noc_xy = (dst >> 36) & 0xFFFF
nx, ny = noc_xy & 0x3F, (noc_xy >> 6) & 0x3F
nocs[(nx, ny)] += 1; phys[(px, py)] += 1
pairs[(px, py)].add((nx, ny))
```
Then ask the diagnostic questions:
- **How many distinct destination cores?** If 1, the writer's
bank-id math collapses to one bank. If `num_cores / 8` or
similar, it's likely walking only one axis of the shard grid.
If exactly `num_cores`, the destinations are at least spread
but might still be permuted.
- **How many distinct source cores write?** If less than expected,
some cores never enter the kernel body — check `cb_wait_front`
/ runtime arg propagation.
- **Per-source: how many destinations does each source touch?**
Most sharded paths have each source writing to exactly one
shard. If the (src → dst) map is the identity (each source
writes to itself), HEIGHT-sharded layouts are matching
source-cores-to-destination-shards correctly. If it's a
transposition (src `(0, K)` → dst `(K, 0)`) you have an x↔y
orientation mismatch — the host's `packed_xy_coords` ordering
and the kernel's `bank_id` derivation disagree on row-major
vs column-major.
The two ends of the kernel-host contract are:
`tt_metal/impl/buffers/tensor_accessor_args.cpp` (host packs
`bank_coords[i] = (coord.x << 8) | coord.y` in the order returned
by `buffer_distribution_spec.cores()`) and
`tt_metal/hw/inc/api/tensor/tensor_accessor.h` (kernel uses
`packed_xy_coords[bank_id]` with `bank_id` derived from
`flattened_shard_id` per `dspec.shard_grid_strides`). When these
disagree on stride ordering, the symptom is the transposition
above. The fix is in whichever side ordered wrong relative to the
silicon contract — typically the upstream specifies row-major and
emule's path needs to match.
## Defer rule
If after ~2 hours of characterization the fix site isn't pinpointed,
write a `roundN-bX-handoff.md` capturing:
- Exact failing tests + tensor shapes
- Corruption pattern (rows, cols, exact-zero vs random-wrong)
- Tile-coord mapping of the corrupted region
- Trace excerpts at each pipeline hop
- Hypotheses ruled in/out
- Suggested next instrumentation
Then pivot to next-priority B-item. Don't guess at fixes — guessed
"fixes" have rolled back successful R-N work in past rounds.
## Critical files
- `include/jit_hw/api/dataflow/dataflow_api.h` — `noc_async_write`,
`noc_async_read` (write-side / read-side trace site)
- `include/jit_hw/llk_pack.h` — `__llk_pack_untilize` (compute scatter)
- `include/jit_hw/api/compute/common.h` — `copy_tile`,
`__emule_unpack_cb_tile_to` (DST fill)
- `include/jit_hw/api/compute/nfaces.h` — `rowmajor_to_nfaces` LUT
- `include/jit_hw/internal/dataflow/dataflow_api_addrgen.h` — extern
bank arrays + `NOC_XY_ADDR` macro
- `include/jit_hw/jit_kernel_stubs.hpp` — extern array declarations
(must match `dataflow_api_addrgen.h`)
- `tt_metal/impl/emulation/emulated_program_runner.cpp` —
`populate_bank_mapping`, `build_kernel_defines`, bank topology
- `tt_metal/impl/emulation/...::Core::l1_ptr` — L1 zero-init
## Methodology lessons from past rounds
- **Misdiagnosis happens when you stop at the first plausible hop.**
A "self-loop NOC" hypothesis was wrong; the real cause was a
topology constant several layers up the chain. Cheap to verify
one hop at a time; expensive to commit to one hypothesis and
build out a fix.
- **The same characterization pattern can have multiple root-cause
classes.** Exact zeros at tile/face boundaries have been bank
topology, extern-array mismatch, AND host scratch overlap in
different rounds. Don't pattern-match to a single class — run
the trace pass.
- **When standard tooling shows "looks correct"**, the trace gate
is probably wrong, not the hypothesis. Re-check iteration index,
per-core fire counts, and whether you're sampling the failing
case.
- **The cross-layer discriminator is the strongest signal.** "Host
wrote X, kernel reads Y" tells you the mutation is between those
two points — independent of how complex the layers in between are.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!