|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Buffers.Binary;
namespace Microsoft.Diagnostics.DataContractReader;
// Linear page cache used by the per-object heap walk.
public sealed class LinearReadCache
{
// Typical page size
private const uint PageSize = 0x1000;
private readonly Target _target;
private readonly byte[] _page = new byte[PageSize];
private ulong _currPageStart;
private uint _currPageSize;
public LinearReadCache(Target target)
{
_target = target;
}
public bool TryReadPointer(ulong addr, out TargetPointer value)
{
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
buffer = buffer.Slice(0, _target.PointerSize);
if (!TryRead(addr, buffer))
{
value = TargetPointer.Null;
return false;
}
value = _target.ReadPointerFromSpan(buffer);
return true;
}
public bool TryReadUInt32(ulong addr, out uint value)
{
Span<byte> buffer = stackalloc byte[sizeof(uint)];
if (!TryRead(addr, buffer))
{
value = 0;
return false;
}
value = _target.IsLittleEndian
? BinaryPrimitives.ReadUInt32LittleEndian(buffer)
: BinaryPrimitives.ReadUInt32BigEndian(buffer);
return true;
}
private bool TryRead(ulong addr, Span<byte> dest)
{
// If the request misses the currently-cached page, try to load the page
// containing it. If that fails (e.g. the page is unmapped), or the request
// straddles the end of the cached page, fall back to a direct read.
if (addr < _currPageStart || addr - _currPageStart >= _currPageSize)
{
if (!MoveToPage(addr))
return DirectRead(addr, dest);
}
ulong offset = addr - _currPageStart;
if (offset + (ulong)dest.Length > _currPageSize)
return DirectRead(addr, dest);
_page.AsSpan((int)offset, dest.Length).CopyTo(dest);
return true;
}
private bool MoveToPage(ulong addr)
{
ulong pageStart = addr - (addr % PageSize);
try
{
_target.ReadBuffer(pageStart, _page.AsSpan(0, (int)PageSize));
_currPageStart = pageStart;
_currPageSize = PageSize;
return true;
}
catch
{
_currPageStart = 0;
_currPageSize = 0;
return false;
}
}
private bool DirectRead(ulong addr, Span<byte> dest)
{
try
{
_target.ReadBuffer(addr, dest);
return true;
}
catch
{
return false;
}
}
}
|