dotnet/BenchmarkDotNet · error · EndOfStreamException

Tried to read {size} bytes for {currentMethod.Signature}, go

Error message

Tried to read {size} bytes for {currentMethod.Signature}, got only {totalBytesRead}

What it means

While reading a method's native bytes for disassembly, the code loops calling DataReader.Read into a buffer sized to map.EndAddress - map.StartAddress. If a Read call returns <= 0 before the full `size` bytes are obtained, EndOfStreamException is thrown. This indicates the target process's memory region could not be fully read: the process exited, the JIT freed/moved the code, or the address range was unmapped mid-read.

Source

Thrown at src/BenchmarkDotNet/Disassemblers/ClrMdDisassembler.cs:222

                Name = method.Signature ?? "",
                NativeCode = method.NativeCode
            };
        }

        private IEnumerable<Asm> Decode(ILToNativeMap map, State state, int depth, ClrMethod currentMethod, DisassemblySyntax syntax)
        {
            ulong startAddress = map.StartAddress;
            uint size = (uint)(map.EndAddress - map.StartAddress);

            byte[] code = new byte[size];

            int totalBytesRead = 0;
            do
            {
                int bytesRead = state.Runtime.DataTarget.DataReader.Read(startAddress + (ulong)totalBytesRead, new Span<byte>(code, totalBytesRead, (int)size - totalBytesRead));
                if (bytesRead <= 0)
                {
                    throw new EndOfStreamException($"Tried to read {size} bytes for {currentMethod.Signature}, got only {totalBytesRead}");
                }
                totalBytesRead += bytesRead;
            } while (totalBytesRead != size);

            return Decode(code, startAddress, state, depth, currentMethod, syntax);
        }

        protected abstract IEnumerable<Asm> Decode(byte[] code, ulong startAddress, State state, int depth, ClrMethod currentMethod, DisassemblySyntax syntax);

        private static ILToNativeMap[] GetCompleteNativeMap(ClrMethod method, ClrRuntime runtime)
        {
            // it's better to use one single map rather than few small ones
            // it's simply easier to get next instruction when decoding ;)

            var hotColdInfo = method.HotColdInfo;
            if (hotColdInfo.HotSize > 0 && hotColdInfo.HotStart > 0)
            {
                return hotColdInfo.ColdSize <= 0

View on GitHub (pinned to b515068b61)

Solutions

  1. Retry the disassembly once the target process is stable; transient reads often succeed on a clean snapshot (use CreateSnapshotAndAttach to reduce races).
  2. Disable Tiered JIT / use a fixed tier so methods are not recompiled during the read (set DOTNET_TieredCompilation=0 in the benchmark job environment).
  3. Filter the disassembly to a stable set of methods and avoid disassembling during teardown.
  4. Ensure the process stays alive for the duration of the read; increase process liveness / run disassembly before the process exits.

Example fix

// before - tiered JIT can move code mid-read
var job = Job.Default.WithEnvironmentVariable("DOTNET_TieredCompilation", "1");

// after - disable tiering so the native map stays valid
var job = Job.Default.WithEnvironmentVariable("DOTNET_TieredCompilation", "0");
Defensive patterns

Strategy: retry

Validate before calling

if (process.HasExited)
    throw new InvalidOperationException("Cannot disassemble: target process has exited.");
// optionally pre-check map validity before reading

Type guard

static bool ProcessAliveForDisasm(Process p) => !p.HasExited;

Try / catch

for (int attempt = 0; attempt < 3; attempt++)
{
    try { return disassembler.AttachAndDisassemble(args); }
    catch (EndOfStreamException) when (attempt < 2)
    {
        // back off briefly and re-snapshot the process before retrying
    }
}
throw;

Prevention

When it happens

Trigger: Disassembling a short-lived or JIT-torn method, reading while the benchmark process is terminating, GC/JIT moving the code after the map was computed, or attaching to a process whose memory is partially paged out/protected.

Common situations: Long-running benchmark suites where the target process exits before disassembly completes, Tiered JIT recompiling a method mid-read, attach-snapshot races, or low-memory/over-committed systems.

Related errors


AI-assisted analysis of dotnet/BenchmarkDotNet@b515068b61 (2026-08-13). Data as JSON: /api/errors/f68ee2014922ed4b. Report an issue: GitHub.