This commit is contained in:
Rachel 2025-07-30 13:25:28 +02:00 committed by GitHub
commit 5d205b8f5d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 286 additions and 71 deletions

View File

@ -43,6 +43,16 @@ namespace
return S_OK;
}
int WriteToTargetCallback(uint64_t addr, const uint8_t* buff, uint32_t count, void* context)
{
ICorDebugMutableDataTarget* target = static_cast<ICorDebugMutableDataTarget*>(context);
HRESULT hr = target->WriteVirtual((CORDB_ADDRESS)addr, buff, count);
if (FAILED(hr))
return hr;
return S_OK;
}
int ReadThreadContext(uint32_t threadId, uint32_t contextFlags, uint32_t contextBufferSize, uint8_t* contextBuffer, void* context)
{
ICorDebugDataTarget* target = reinterpret_cast<ICorDebugDataTarget*>(context);
@ -54,7 +64,7 @@ namespace
}
}
CDAC CDAC::Create(uint64_t descriptorAddr, ICorDebugDataTarget* target, IUnknown* legacyImpl)
CDAC CDAC::Create(uint64_t descriptorAddr, ICorDebugMutableDataTarget* target, IUnknown* legacyImpl)
{
HMODULE cdacLib;
if (!TryLoadCDACLibrary(&cdacLib))
@ -64,7 +74,7 @@ CDAC CDAC::Create(uint64_t descriptorAddr, ICorDebugDataTarget* target, IUnknown
_ASSERTE(init != nullptr);
intptr_t handle;
if (init(descriptorAddr, &ReadFromTargetCallback, &ReadThreadContext, target, &handle) != 0)
if (init(descriptorAddr, &ReadFromTargetCallback, &WriteToTargetCallback, &ReadThreadContext, target, &handle) != 0)
{
::FreeLibrary(cdacLib);
return {};

View File

@ -7,7 +7,7 @@
class CDAC final
{
public: // static
static CDAC Create(uint64_t descriptorAddr, ICorDebugDataTarget *pDataTarget, IUnknown* legacyImpl);
static CDAC Create(uint64_t descriptorAddr, ICorDebugMutableDataTarget *pDataTarget, IUnknown* legacyImpl);
public:
CDAC() = default;

View File

@ -7033,7 +7033,7 @@ CLRDataCreateInstance(REFIID iid,
HRESULT qiRes = pClrDataAccess->QueryInterface(IID_IUnknown, (void**)&thisImpl);
_ASSERTE(SUCCEEDED(qiRes));
CDAC& cdac = pClrDataAccess->m_cdac;
cdac = CDAC::Create(contractDescriptorAddr, pClrDataAccess->m_pTarget, thisImpl);
cdac = CDAC::Create(contractDescriptorAddr, pClrDataAccess->m_pMutableTarget, thisImpl);
if (cdac.IsValid())
{
// Get SOS interfaces from the cDAC if available.

View File

@ -72,6 +72,13 @@ public abstract class Target
/// <param name="buffer">Destination to copy the bytes, the number of bytes to read is the span length</param>
public abstract void ReadBuffer(ulong address, Span<byte> buffer);
/// <summary>
/// Write some bytes to the target
/// </summary>
/// <param name="address">The address where to start writing</param>
/// <param name="buffer">Source of the bytes to write, the number of bytes to write is the span length</param>
public abstract void WriteBuffer(ulong address, Span<byte> buffer);
/// <summary>
/// Read a null-terminated UTF-8 string from the target
/// </summary>
@ -133,6 +140,15 @@ public abstract class Target
/// <returns>Value read from the target</returns>
public abstract T Read<T>(ulong address) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>;
/// <summary>
/// Write a value to the target in target endianness
/// </summary>
/// <typeparam name="T">Type of value to write</typeparam>
/// <param name="address">Address to start writing to</param>
/// <param name="value">Value to write</param>
/// <returns>True if the write was successful, false otherwise</returns>
public abstract bool Write<T>(ulong address, T value) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>;
/// <summary>
/// Read a target pointer from a span of bytes
/// </summary>

View File

@ -32,8 +32,7 @@ public sealed unsafe class ContractDescriptorTarget : Target
}
private readonly Configuration _config;
private readonly Reader _reader;
private readonly DataTargetDelegates _dataTargetDelegates;
private readonly Dictionary<string, int> _contracts = [];
private readonly IReadOnlyDictionary<string, GlobalValue> _globals = new Dictionary<string, GlobalValue>();
private readonly Dictionary<DataType, Target.TypeInfo> _knownTypes = [];
@ -43,6 +42,7 @@ public sealed unsafe class ContractDescriptorTarget : Target
public override DataCache ProcessedData { get; }
public delegate int ReadFromTargetDelegate(ulong address, Span<byte> bufferToFill);
public delegate int WriteToTargetDelegate(ulong address, Span<byte> bufferToWrite);
public delegate int GetTargetThreadContextDelegate(uint threadId, uint contextFlags, Span<byte> bufferToFill);
/// <summary>
@ -56,18 +56,19 @@ public sealed unsafe class ContractDescriptorTarget : Target
public static bool TryCreate(
ulong contractDescriptor,
ReadFromTargetDelegate readFromTarget,
WriteToTargetDelegate writeToTarget,
GetTargetThreadContextDelegate getThreadContext,
[NotNullWhen(true)] out ContractDescriptorTarget? target)
{
Reader reader = new Reader(readFromTarget, getThreadContext);
DataTargetDelegates dataTargetDelegates = new DataTargetDelegates(readFromTarget, writeToTarget, getThreadContext);
if (TryReadContractDescriptor(
contractDescriptor,
reader,
dataTargetDelegates,
out Configuration config,
out ContractDescriptorParser.ContractDescriptor? descriptor,
out TargetPointer[] pointerData))
{
target = new ContractDescriptorTarget(config, descriptor!, pointerData, reader);
target = new ContractDescriptorTarget(config, descriptor!, pointerData, dataTargetDelegates);
return true;
}
@ -89,6 +90,7 @@ public sealed unsafe class ContractDescriptorTarget : Target
ContractDescriptorParser.ContractDescriptor contractDescriptor,
TargetPointer[] globalPointerValues,
ReadFromTargetDelegate readFromTarget,
WriteToTargetDelegate writeToTarget,
GetTargetThreadContextDelegate getThreadContext,
bool isLittleEndian,
int pointerSize)
@ -97,15 +99,15 @@ public sealed unsafe class ContractDescriptorTarget : Target
new Configuration { IsLittleEndian = isLittleEndian, PointerSize = pointerSize },
contractDescriptor,
globalPointerValues,
new Reader(readFromTarget, getThreadContext));
new DataTargetDelegates(readFromTarget, writeToTarget, getThreadContext));
}
private ContractDescriptorTarget(Configuration config, ContractDescriptorParser.ContractDescriptor descriptor, TargetPointer[] pointerData, Reader reader)
private ContractDescriptorTarget(Configuration config, ContractDescriptorParser.ContractDescriptor descriptor, TargetPointer[] pointerData, DataTargetDelegates dataTargetDelegates)
{
Contracts = new CachingContractRegistry(this, this.TryGetContractVersion);
ProcessedData = new DataCache(this);
_config = config;
_reader = reader;
_dataTargetDelegates = dataTargetDelegates;
_contracts = descriptor.Contracts ?? [];
@ -187,7 +189,7 @@ public sealed unsafe class ContractDescriptorTarget : Target
// See docs/design/datacontracts/contract-descriptor.md
private static bool TryReadContractDescriptor(
ulong address,
Reader reader,
DataTargetDelegates dataTargetDelegates,
out Configuration config,
out ContractDescriptorParser.ContractDescriptor? descriptor,
out TargetPointer[] pointerData)
@ -198,7 +200,7 @@ public sealed unsafe class ContractDescriptorTarget : Target
// Magic - uint64_t
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
if (reader.ReadFromTarget(address, buffer) < 0)
if (dataTargetDelegates.ReadFromTarget(address, buffer) < 0)
return false;
address += sizeof(ulong);
@ -209,7 +211,7 @@ public sealed unsafe class ContractDescriptorTarget : Target
return false;
// Flags - uint32_t
if (!TryRead(address, isLittleEndian, reader, out uint flags))
if (!TryRead(address, isLittleEndian, dataTargetDelegates, out uint flags))
return false;
address += sizeof(uint);
@ -220,19 +222,19 @@ public sealed unsafe class ContractDescriptorTarget : Target
config = new Configuration { IsLittleEndian = isLittleEndian, PointerSize = pointerSize };
// Descriptor size - uint32_t
if (!TryRead(address, config.IsLittleEndian, reader, out uint descriptorSize))
if (!TryRead(address, config.IsLittleEndian, dataTargetDelegates, out uint descriptorSize))
return false;
address += sizeof(uint);
// Descriptor - char*
if (!TryReadPointer(address, config, reader, out TargetPointer descriptorAddr))
if (!TryReadPointer(address, config, dataTargetDelegates, out TargetPointer descriptorAddr))
return false;
address += (uint)pointerSize;
// Pointer data count - uint32_t
if (!TryRead(address, config.IsLittleEndian, reader, out uint pointerDataCount))
if (!TryRead(address, config.IsLittleEndian, dataTargetDelegates, out uint pointerDataCount))
return false;
address += sizeof(uint);
@ -241,14 +243,14 @@ public sealed unsafe class ContractDescriptorTarget : Target
address += sizeof(uint);
// Pointer data - uintptr_t*
if (!TryReadPointer(address, config, reader, out TargetPointer pointerDataAddr))
if (!TryReadPointer(address, config, dataTargetDelegates, out TargetPointer pointerDataAddr))
return false;
// Read descriptor
Span<byte> descriptorBuffer = descriptorSize <= StackAllocByteThreshold
? stackalloc byte[(int)descriptorSize]
: new byte[(int)descriptorSize];
if (reader.ReadFromTarget(descriptorAddr.Value, descriptorBuffer) < 0)
if (dataTargetDelegates.ReadFromTarget(descriptorAddr.Value, descriptorBuffer) < 0)
return false;
descriptor = ContractDescriptorParser.ParseCompact(descriptorBuffer);
@ -259,7 +261,7 @@ public sealed unsafe class ContractDescriptorTarget : Target
pointerData = new TargetPointer[pointerDataCount];
for (int i = 0; i < pointerDataCount; i++)
{
if (!TryReadPointer(pointerDataAddr.Value + (uint)(i * pointerSize), config, reader, out pointerData[i]))
if (!TryReadPointer(pointerDataAddr.Value + (uint)(i * pointerSize), config, dataTargetDelegates, out pointerData[i]))
return false;
}
@ -280,7 +282,7 @@ public sealed unsafe class ContractDescriptorTarget : Target
public override bool TryGetThreadContext(ulong threadId, uint contextFlags, Span<byte> buffer)
{
// Underlying API only supports 32-bit thread IDs, mask off top 32 bits
int hr = _reader.GetThreadContext((uint)(threadId & uint.MaxValue), contextFlags, buffer);
int hr = _dataTargetDelegates.GetThreadContext((uint)(threadId & uint.MaxValue), contextFlags, buffer);
return hr == 0;
}
@ -292,17 +294,17 @@ public sealed unsafe class ContractDescriptorTarget : Target
/// <returns>Value read from the target</returns>
public override T Read<T>(ulong address)
{
if (!TryRead(address, _config.IsLittleEndian, _reader, out T value))
if (!TryRead(address, _config.IsLittleEndian, _dataTargetDelegates, out T value))
throw new InvalidOperationException($"Failed to read {typeof(T)} at 0x{address:x8}.");
return value;
}
private static bool TryRead<T>(ulong address, bool isLittleEndian, Reader reader, out T value) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
private static bool TryRead<T>(ulong address, bool isLittleEndian, DataTargetDelegates dataTargetDelegates, out T value) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
{
value = default;
Span<byte> buffer = stackalloc byte[sizeof(T)];
if (reader.ReadFromTarget(address, buffer) < 0)
if (dataTargetDelegates.ReadFromTarget(address, buffer) < 0)
return false;
return isLittleEndian
@ -310,6 +312,32 @@ public sealed unsafe class ContractDescriptorTarget : Target
: T.TryReadBigEndian(buffer, !IsSigned<T>(), out value);
}
/// <summary>
/// Write a value to the target in target endianness
/// </summary>
/// <typeparam name="T">Type of value to write</typeparam>
/// <param name="address">Address to start writing to</param>
/// <returns>True if the value is successfully written. Throws an InvalidOperationException otherwise.</returns>
public override bool Write<T>(ulong address, T value)
{
if (!TryWrite(address, _config.IsLittleEndian, _dataTargetDelegates, value))
throw new InvalidOperationException($"Failed to write {typeof(T)} at 0x{address:x8}.");
return true;
}
private static bool TryWrite<T>(ulong address, bool isLittleEndian, DataTargetDelegates dataTargetDelegates, T value) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
{
Span<byte> buffer = stackalloc byte[sizeof(T)];
int bytesWritten = default;
bool success = isLittleEndian
? value.TryWriteLittleEndian(buffer, out bytesWritten)
: value.TryWriteBigEndian(buffer, out bytesWritten);
if (!success || bytesWritten != buffer.Length || dataTargetDelegates.WriteToTarget(address, buffer) < 0)
return false;
return true;
}
private static T Read<T>(ReadOnlySpan<byte> bytes, bool isLittleEndian) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
{
if (sizeof(T) != bytes.Length)
@ -335,7 +363,18 @@ public sealed unsafe class ContractDescriptorTarget : Target
private bool TryReadBuffer(ulong address, Span<byte> buffer)
{
return _reader.ReadFromTarget(address, buffer) >= 0;
return _dataTargetDelegates.ReadFromTarget(address, buffer) >= 0;
}
public override void WriteBuffer(ulong address, Span<byte> buffer)
{
if (!TryWriteBuffer(address, buffer))
throw new InvalidOperationException($"Failed to write {buffer.Length} bytes at 0x{address:x8}.");
}
private bool TryWriteBuffer(ulong address, Span<byte> buffer)
{
return _dataTargetDelegates.WriteToTarget(address, buffer) >= 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -351,7 +390,7 @@ public sealed unsafe class ContractDescriptorTarget : Target
/// <returns>Pointer read from the target</returns>}
public override TargetPointer ReadPointer(ulong address)
{
if (!TryReadPointer(address, _config, _reader, out TargetPointer pointer))
if (!TryReadPointer(address, _config, _dataTargetDelegates, out TargetPointer pointer))
throw new InvalidOperationException($"Failed to read pointer at 0x{address:x8}.");
return pointer;
@ -456,33 +495,33 @@ public sealed unsafe class ContractDescriptorTarget : Target
/// <returns>Value read from the target</returns>
public override TargetNUInt ReadNUInt(ulong address)
{
if (!TryReadNUInt(address, _config, _reader, out ulong value))
if (!TryReadNUInt(address, _config, _dataTargetDelegates, out ulong value))
throw new InvalidOperationException($"Failed to read nuint at 0x{address:x8}.");
return new TargetNUInt(value);
}
private static bool TryReadPointer(ulong address, Configuration config, Reader reader, out TargetPointer pointer)
private static bool TryReadPointer(ulong address, Configuration config, DataTargetDelegates dataTargetDelegates, out TargetPointer pointer)
{
pointer = TargetPointer.Null;
if (!TryReadNUInt(address, config, reader, out ulong value))
if (!TryReadNUInt(address, config, dataTargetDelegates, out ulong value))
return false;
pointer = new TargetPointer(value);
return true;
}
private static bool TryReadNUInt(ulong address, Configuration config, Reader reader, out ulong value)
private static bool TryReadNUInt(ulong address, Configuration config, DataTargetDelegates dataTargetDelegates, out ulong value)
{
value = 0;
if (config.PointerSize == sizeof(uint)
&& TryRead(address, config.IsLittleEndian, reader, out uint value32))
&& TryRead(address, config.IsLittleEndian, dataTargetDelegates, out uint value32))
{
value = value32;
return true;
}
else if (config.PointerSize == sizeof(ulong)
&& TryRead(address, config.IsLittleEndian, reader, out ulong value64))
&& TryRead(address, config.IsLittleEndian, dataTargetDelegates, out ulong value64))
{
value = value64;
return true;
@ -657,8 +696,9 @@ public sealed unsafe class ContractDescriptorTarget : Target
}
}
private readonly struct Reader(
private readonly struct DataTargetDelegates(
ReadFromTargetDelegate readFromTarget,
WriteToTargetDelegate writeToTarget,
GetTargetThreadContextDelegate getThreadContext)
{
public int ReadFromTarget(ulong address, Span<byte> buffer)
@ -673,5 +713,9 @@ public sealed unsafe class ContractDescriptorTarget : Target
{
return getThreadContext(threadId, contextFlags, buffer);
}
public int WriteToTarget(ulong address, Span<byte> buffer)
{
return writeToTarget(address, buffer);
}
}
}

View File

@ -18,6 +18,7 @@ extern "C"
int cdac_reader_init(
uint64_t descriptor,
int(*read_from_target)(uint64_t, uint8_t*, uint32_t, void*),
int(*write_to_target)(uint64_t, const uint8_t*, uint32_t, void*),
int(*read_thread_context)(uint32_t, uint32_t, uint32_t, uint8_t*, void*),
void* read_context,
/*out*/ intptr_t* handle);

View File

@ -16,8 +16,9 @@ internal static class Entrypoints
private static unsafe int Init(
ulong descriptor,
delegate* unmanaged<ulong, byte*, uint, void*, int> readFromTarget,
delegate* unmanaged<ulong, byte*, uint, void*, int> writeToTarget,
delegate* unmanaged<uint, uint, uint, byte*, void*, int> readThreadContext,
void* readContext,
void* delegateContext,
IntPtr* handle)
{
// TODO: [cdac] Better error code/details
@ -27,14 +28,21 @@ internal static class Entrypoints
{
fixed (byte* bufferPtr = buffer)
{
return readFromTarget(address, bufferPtr, (uint)buffer.Length, readContext);
return readFromTarget(address, bufferPtr, (uint)buffer.Length, delegateContext);
}
},
(address, buffer) =>
{
fixed (byte* bufferPtr = buffer)
{
return writeToTarget(address, bufferPtr, (uint)buffer.Length, delegateContext);
}
},
(threadId, contextFlags, buffer) =>
{
fixed (byte* bufferPtr = buffer)
{
return readThreadContext(threadId, contextFlags, (uint)buffer.Length, bufferPtr, readContext);
return readThreadContext(threadId, contextFlags, (uint)buffer.Length, bufferPtr, delegateContext);
}
},
out ContractDescriptorTarget? target))
@ -124,6 +132,14 @@ internal static class Entrypoints
return dataTarget.ReadVirtual(address, bufferPtr, (uint)buffer.Length, &bytesRead);
}
},
(address, buffer) =>
{
fixed (byte* bufferPtr = buffer)
{
uint bytesWritten;
return dataTarget.WriteVirtual(address, bufferPtr, (uint)buffer.Length, &bytesWritten);
}
},
(threadId, contextFlags, bufferToFill) =>
{
fixed (byte* bufferPtr = bufferToFill)

View File

@ -11,6 +11,15 @@ namespace Microsoft.Diagnostics.DataContractReader.Legacy;
// See src/coreclr/inc/sospriv.idl
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
internal enum CLRDataOtherNotifyFlag
{
CLRDATA_NOTIFY_ON_MODULE_LOAD = 0x1,
CLRDATA_NOTIFY_ON_MODULE_UNLOAD = 0x2,
CLRDATA_NOTIFY_ON_EXCEPTION = 0x4,
CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER = 0x8
}
internal struct DacpThreadStoreData
{
public int threadCount;

View File

@ -233,7 +233,55 @@ internal sealed unsafe partial class SOSDacImpl : IXCLRDataProcess, IXCLRDataPro
return hr;
}
int IXCLRDataProcess.SetOtherNotificationFlags(uint flags)
=> _legacyProcess is not null ? _legacyProcess.SetOtherNotificationFlags(flags) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
try
{
if ((flags & ~((uint)CLRDataOtherNotifyFlag.CLRDATA_NOTIFY_ON_MODULE_LOAD |
(uint)CLRDataOtherNotifyFlag.CLRDATA_NOTIFY_ON_MODULE_UNLOAD |
(uint)CLRDataOtherNotifyFlag.CLRDATA_NOTIFY_ON_EXCEPTION |
(uint)CLRDataOtherNotifyFlag.CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER)) != 0)
{
hr = HResults.E_INVALIDARG;
}
else
{
TargetPointer dacNotificationFlags = _target.ReadGlobalPointer(Constants.Globals.DacNotificationFlags);
_target.Write<uint>(dacNotificationFlags, flags);
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacyProcess is not null)
{
int hrLocal = default;
uint flagsLocal = default;
// have to read the flags like this and not with GetOtherNotificationFlags
// because the legacy DAC cache will not be updated when we set the flags in cDAC
// so we need to verify without using the legacy DAC
hrLocal = HResults.S_OK;
try
{
flagsLocal = _target.Read<uint>(_target.ReadGlobalPointer(Constants.Globals.DacNotificationFlags));
}
catch (System.Exception ex)
{
hrLocal = ex.HResult;
}
Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
if (hr == HResults.S_OK)
{
Debug.Assert(flags == flagsLocal);
}
// update the DAC cache
_legacyProcess.SetOtherNotificationFlags(flags);
}
#endif
return hr;
}
int IXCLRDataProcess.StartEnumMethodDefinitionsByAddress(ClrDataAddress address, ulong* handle)
=> _legacyProcess is not null ? _legacyProcess.StartEnumMethodDefinitionsByAddress(address, handle) : HResults.E_NOTIMPL;

View File

@ -145,7 +145,7 @@ public class CodeVersionsTests
TestPlaceholderTarget target = new TestPlaceholderTarget(
arch,
builder.Builder.GetReadContext().ReadFromTarget,
builder.Builder.GetMemoryContext().ReadFromTarget,
builder.Types);
IContractFactory<ICodeVersions> cvfactory = new CodeVersionsFactory();

View File

@ -178,7 +178,7 @@ internal class ContractDescriptorBuilder : MockMemorySpace.Builder
if (_created)
throw new InvalidOperationException("Context already created");
ulong contractDescriptorAddress = CreateDescriptorFragments();
MockMemorySpace.ReadContext context = GetReadContext();
return ContractDescriptorTarget.TryCreate(contractDescriptorAddress, context.ReadFromTarget, null, out target);
MockMemorySpace.MemoryContext memoryContext = GetMemoryContext();
return ContractDescriptorTarget.TryCreate(contractDescriptorAddress, memoryContext.ReadFromTarget, memoryContext.WriteToTarget, null, out target);
}
}

View File

@ -183,6 +183,25 @@ public unsafe class TargetTests
Assert.Equal(expected, actual);
}
[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void WriteValue(MockTarget.Architecture arch)
{
TargetTestHelpers targetTestHelpers = new(arch);
ContractDescriptorBuilder builder = new(targetTestHelpers);
uint expected = 0xdeadbeef;
ulong addr = 0x1000;
MockMemorySpace.HeapFragment fragment = new() { Address = addr, Data = new byte[4] };
builder.AddHeapFragment(fragment);
bool success = builder.TryCreateTarget(out ContractDescriptorTarget? target);
Assert.True(success);
bool writeSuccess = target.Write<uint>(addr, expected);
Assert.True(writeSuccess);
Assert.Equal(expected, target.Read<uint>(addr));
}
[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void ReadUtf16String(MockTarget.Architecture arch)

View File

@ -42,7 +42,7 @@ public class DacStreamsTests
builder = configure(builder);
}
var target = new TestPlaceholderTarget(arch, builder.GetReadContext().ReadFromTarget, DacStreamsTypes, DacStreamsGlobals);
var target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget, DacStreamsTypes, DacStreamsGlobals);
target.SetContracts(Mock.Of<ContractRegistry>(
c => c.DacStreams == ((IContractFactory<IDacStreams>)new DacStreamsFactory()).CreateContract(target, 1)));

View File

@ -14,7 +14,7 @@ public class ExecutionManagerTests
private static Target CreateTarget(MockDescriptors.ExecutionManager emBuilder)
{
var arch = emBuilder.Builder.TargetTestHelpers.Arch;
TestPlaceholderTarget.ReadFromTargetDelegate reader = emBuilder.Builder.GetReadContext().ReadFromTarget;
TestPlaceholderTarget.ReadFromTargetDelegate reader = emBuilder.Builder.GetMemoryContext().ReadFromTarget;
var target = new TestPlaceholderTarget(arch, reader, emBuilder.Types, emBuilder.Globals);
IContractFactory<IExecutionManager> emfactory = new ExecutionManagerFactory();
ContractRegistry reg = Mock.Of<ContractRegistry>(

View File

@ -25,7 +25,7 @@ public class HashMapTests
TargetPointer mapAddress = hashMap.CreateMap(entries);
TargetPointer ptrMapAddress = hashMap.CreatePtrMap(entries);
Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, hashMap.Types, hashMap.Globals);
Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetMemoryContext().ReadFromTarget, hashMap.Types, hashMap.Globals);
var lookup = HashMapLookup.Create(target);
var ptrLookup = PtrHashMapLookup.Create(target);
@ -61,7 +61,7 @@ public class HashMapTests
TargetPointer mapAddress = hashMap.CreateMap(entries);
TargetPointer ptrMapAddress = hashMap.CreatePtrMap(entries);
Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, hashMap.Types, hashMap.Globals);
Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetMemoryContext().ReadFromTarget, hashMap.Types, hashMap.Globals);
var lookup = HashMapLookup.Create(target);
var ptrLookup = PtrHashMapLookup.Create(target);
@ -86,7 +86,7 @@ public class HashMapTests
TargetPointer mapAddress = hashMap.CreateMap(entries);
TargetPointer ptrMapAddress = hashMap.CreatePtrMap(entries);
Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, hashMap.Types, hashMap.Globals);
Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetMemoryContext().ReadFromTarget, hashMap.Types, hashMap.Globals);
{
var lookup = HashMapLookup.Create(target);

View File

@ -11,11 +11,11 @@ public class NibbleMapTestsBase
{
internal static Target CreateTarget(NibbleMapTestBuilderBase nibbleMapTestBuilder)
{
MockMemorySpace.ReadContext readContext = new()
MockMemorySpace.MemoryContext memoryContext = new()
{
HeapFragments = new[] { nibbleMapTestBuilder.NibbleMapFragment }
};
return new TestPlaceholderTarget(nibbleMapTestBuilder.Arch, readContext.ReadFromTarget);
return new TestPlaceholderTarget(nibbleMapTestBuilder.Arch, memoryContext.ReadFromTarget);
}
}

View File

@ -15,7 +15,7 @@ public class RangeSectionMapTests
public void TestLookupFail(MockTarget.Architecture arch)
{
var builder = MockDescriptors.ExecutionManager.CreateRangeSection(arch);
var target = new TestPlaceholderTarget(arch, builder.GetReadContext().ReadFromTarget);
var target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget);
var rsla = RangeSectionMap.Create(target);
@ -33,7 +33,7 @@ public class RangeSectionMapTests
var length = 0x1000u;
var value = 0x0a0a_0a0au;
builder.InsertAddressRange(inputPC, length, value);
var target = new TestPlaceholderTarget(arch, builder.GetReadContext().ReadFromTarget);
var target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget);
var rsla = RangeSectionMap.Create(target);
@ -48,7 +48,7 @@ public class RangeSectionMapTests
public void TestGetIndexForLevel(MockTarget.Architecture arch)
{
// Exhaustively test GetIndexForLevel for all possible values of the byte for each level
var target = new TestPlaceholderTarget(arch, new MockMemorySpace.ReadContext().ReadFromTarget);
var target = new TestPlaceholderTarget(arch, new MockMemorySpace.MemoryContext().ReadFromTarget);
var rsla = RangeSectionMap.Create(target);
int numLevels = arch.Is64Bit ? 5 : 2;
// the bits 0..effectiveRange - 1 are not handled the map and are irrelevant

View File

@ -34,7 +34,7 @@ public class RuntimeFunctionTests
uint[] entries = [0x100, 0x1f0, 0x1000, 0x2000, 0xa000];
TargetPointer addr = runtimeFunctions.AddRuntimeFunctions(entries);
Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, runtimeFunctions.Types);
Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetMemoryContext().ReadFromTarget, runtimeFunctions.Types);
RuntimeFunctionLookup lookup = RuntimeFunctionLookup.Create(target);
for (uint i = 0; i < entries.Length; i++)
@ -59,7 +59,7 @@ public class RuntimeFunctionTests
uint[] entries = [0x100, 0x1f0, 0x1000, 0x2000, 0xa000];
TargetPointer addr = runtimeFunctions.AddRuntimeFunctions(entries);
TestPlaceholderTarget target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, runtimeFunctions.Types);
TestPlaceholderTarget target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetMemoryContext().ReadFromTarget, runtimeFunctions.Types);
ContractRegistry reg = Mock.Of<ContractRegistry>(
c => c.PlatformMetadata == new Mock<Contracts.IPlatformMetadata>().Object);
target.SetContracts(reg);
@ -84,7 +84,7 @@ public class RuntimeFunctionTests
uint[] entries = [0x100, 0x1f0];
TargetPointer addr = runtimeFunctions.AddRuntimeFunctions(entries);
TestPlaceholderTarget target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, runtimeFunctions.Types);
TestPlaceholderTarget target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetMemoryContext().ReadFromTarget, runtimeFunctions.Types);
ContractRegistry reg = Mock.Of<ContractRegistry>(
c => c.PlatformMetadata == new Mock<Contracts.IPlatformMetadata>().Object);
target.SetContracts(reg);

View File

@ -28,7 +28,7 @@ public unsafe class LoaderTests
TargetPointer moduleAddr = loader.AddModule(path: expected);
TargetPointer moduleAddrEmptyPath = loader.AddModule();
var target = new TestPlaceholderTarget(arch, builder.GetReadContext().ReadFromTarget, loader.Types);
var target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget, loader.Types);
target.SetContracts(Mock.Of<ContractRegistry>(
c => c.Loader == ((IContractFactory<ILoader>)new LoaderFactory()).CreateContract(target, 1)));
@ -62,7 +62,7 @@ public unsafe class LoaderTests
TargetPointer moduleAddr = loader.AddModule(fileName: expected);
TargetPointer moduleAddrEmptyName = loader.AddModule();
var target = new TestPlaceholderTarget(arch, builder.GetReadContext().ReadFromTarget, loader.Types);
var target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget, loader.Types);
target.SetContracts(Mock.Of<ContractRegistry>(
c => c.Loader == ((IContractFactory<ILoader>)new LoaderFactory()).CreateContract(target, 1)));

View File

@ -16,7 +16,7 @@ public class MethodDescTests
private static Target CreateTarget(MockDescriptors.MethodDescriptors methodDescBuilder, Mock<IExecutionManager> mockExecutionManager = null)
{
MockMemorySpace.Builder builder = methodDescBuilder.Builder;
var target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, methodDescBuilder.Types, methodDescBuilder.Globals);
var target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetMemoryContext().ReadFromTarget, methodDescBuilder.Types, methodDescBuilder.Globals);
mockExecutionManager ??= new Mock<IExecutionManager>();
target.SetContracts(Mock.Of<ContractRegistry>(

View File

@ -21,7 +21,7 @@ public class MethodTableTests
configure?.Invoke(rtsBuilder);
var target = new TestPlaceholderTarget(arch, builder.GetReadContext().ReadFromTarget, rtsBuilder.Types, rtsBuilder.Globals);
var target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget, rtsBuilder.Types, rtsBuilder.Globals);
target.SetContracts(Mock.Of<ContractRegistry>(
c => c.RuntimeTypeSystem == ((IContractFactory<IRuntimeTypeSystem>)new RuntimeTypeSystemFactory()).CreateContract(target, 1)));

View File

@ -166,9 +166,9 @@ internal partial class MockDescriptors
} while (cur.Value < end);
}
public MockMemorySpace.ReadContext GetReadContext()
public MockMemorySpace.MemoryContext GetMemoryContext()
{
return _builder.GetReadContext();
return _builder.GetMemoryContext();
}
}

View File

@ -47,7 +47,7 @@ internal unsafe static partial class MockMemorySpace
{
foreach (var fragment in _heapFragments)
{
if (address >= fragment.Address && address+(ulong)length <= fragment.Address + (ulong)fragment.Data.Length)
if (address >= fragment.Address && address + (ulong)length <= fragment.Address + (ulong)fragment.Data.Length)
return fragment.Data.AsSpan((int)(address - fragment.Address), length);
}
throw new InvalidOperationException($"No fragment includes addresses from 0x{address:x} with length {length}");
@ -83,9 +83,9 @@ internal unsafe static partial class MockMemorySpace
return this;
}
internal ReadContext GetReadContext()
internal MemoryContext GetMemoryContext()
{
ReadContext context = new ReadContext
MemoryContext context = new MemoryContext
{
HeapFragments = _heapFragments,
};
@ -124,9 +124,9 @@ internal unsafe static partial class MockMemorySpace
}
// Used by ReadFromTarget to return the appropriate bytes
internal class ReadContext
internal class MemoryContext
{
public IReadOnlyList<HeapFragment> HeapFragments { get; init; }
public IList<HeapFragment> HeapFragments { get; init; }
internal int ReadFromTarget(ulong address, Span<byte> buffer)
{
@ -173,5 +173,54 @@ internal unsafe static partial class MockMemorySpace
throw new InvalidOperationException($"Not enough data in fragment at {lastHeapFragment.Address:X} ('{lastHeapFragment.Name}') to read {buffer.Length} bytes at {address:X} (only {availableLength} bytes available)");
return -1;
}
internal int WriteToTarget(ulong address, Span<byte> buffer)
{
if (buffer.Length == 0)
return 0;
if (address == 0)
return -1;
bool partialWriteOccurred = false;
HeapFragment lastHeapFragment = default;
int availableLength = 0;
while (true)
{
bool tryAgain = false;
for (int i = 0; i < HeapFragments.Count; i++)
{
var fragment = HeapFragments[i];
if (address >= fragment.Address && address < fragment.Address + (ulong)fragment.Data.Length)
{
int offset = (int)(address - fragment.Address);
availableLength = fragment.Data.Length - offset;
if (availableLength >= buffer.Length)
{
buffer.CopyTo(fragment.Data.AsSpan(offset, buffer.Length));
HeapFragments[i] = fragment; // Update the fragment in the list
return 0;
}
else
{
lastHeapFragment = fragment;
partialWriteOccurred = true;
tryAgain = true;
buffer.Slice(0, availableLength).CopyTo(fragment.Data.AsSpan(offset, availableLength));
HeapFragments[i] = fragment; // Update the fragment in the list
buffer = buffer.Slice(availableLength);
address = fragment.Address + (ulong)fragment.Data.Length;
break;
}
}
}
if (!tryAgain)
break;
}
if (partialWriteOccurred)
throw new InvalidOperationException($"Not enough space in fragment at {lastHeapFragment.Address:X} ('{lastHeapFragment.Name}') to write {buffer.Length} bytes at {address:X} (only {availableLength} bytes available)");
return -1;
}
}
}

View File

@ -22,7 +22,7 @@ public unsafe class ObjectTests
configure?.Invoke(objectBuilder);
var target = new TestPlaceholderTarget(arch, builder.GetReadContext().ReadFromTarget, objectBuilder.Types, objectBuilder.Globals);
var target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget, objectBuilder.Types, objectBuilder.Globals);
target.SetContracts(Mock.Of<ContractRegistry>(
c => c.Object == ((IContractFactory<IObject>)new ObjectFactory()).CreateContract(target, 1)
&& c.RuntimeTypeSystem == ((IContractFactory<IRuntimeTypeSystem>)new RuntimeTypeSystemFactory()).CreateContract(target, 1)));

View File

@ -348,7 +348,7 @@ public class PrecodeStubsTests
private static Target CreateTarget(PrecodeBuilder precodeBuilder)
{
var arch = precodeBuilder.Builder.TargetTestHelpers.Arch;
TestPlaceholderTarget.ReadFromTargetDelegate reader = precodeBuilder.Builder.GetReadContext().ReadFromTarget;
TestPlaceholderTarget.ReadFromTargetDelegate reader = precodeBuilder.Builder.GetMemoryContext().ReadFromTarget;
// hack for this test put the precode machine descriptor at the same address as the PlatformMetadata
(string Name, ulong Value)[] globals = [(Constants.Globals.PlatformMetadata, precodeBuilder.MachineDescriptorAddress)];
var target = new TestPlaceholderTarget(arch, reader, precodeBuilder.Types, globals);

View File

@ -20,7 +20,7 @@ public class ReJITTests
MockReJIT builder,
Mock<ICodeVersions> mockCodeVersions = null)
{
TestPlaceholderTarget target = new TestPlaceholderTarget(arch, builder.Builder.GetReadContext().ReadFromTarget, builder.Types, builder.Globals);
TestPlaceholderTarget target = new TestPlaceholderTarget(arch, builder.Builder.GetMemoryContext().ReadFromTarget, builder.Types, builder.Globals);
mockCodeVersions ??= new Mock<ICodeVersions>();

View File

@ -16,7 +16,7 @@ public class RuntimeInfoTests
(string Name, string Value)[] globalStrings)
{
MockMemorySpace.Builder builder = new MockMemorySpace.Builder(new TargetTestHelpers(arch));
TestPlaceholderTarget target = new TestPlaceholderTarget(arch, builder.GetReadContext().ReadFromTarget, [], [], globalStrings);
TestPlaceholderTarget target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget, [], [], globalStrings);
IContractFactory<IRuntimeInfo> runtimeInfoFactory = new RuntimeInfoFactory();

View File

@ -83,6 +83,7 @@ internal class TestPlaceholderTarget : Target
if (_dataReader(address, buffer) < 0)
throw new InvalidOperationException($"Failed to read {buffer.Length} bytes at 0x{address:x8}.");
}
public override void WriteBuffer(ulong address, Span<byte> buffer) => throw new NotImplementedException();
public override string ReadUtf8String(ulong address) => throw new NotImplementedException();
public override string ReadUtf16String(ulong address)
@ -160,6 +161,7 @@ internal class TestPlaceholderTarget : Target
}
public override T Read<T>(ulong address) => DefaultRead<T>(address);
public override bool Write<T>(ulong address, T value) => throw new NotImplementedException();
#region subclass reader helpers

View File

@ -13,7 +13,7 @@ public unsafe class ThreadTests
private static Target CreateTarget(MockDescriptors.Thread thread)
{
MockTarget.Architecture arch = thread.Builder.TargetTestHelpers.Arch;
var target = new TestPlaceholderTarget(arch, thread.Builder.GetReadContext().ReadFromTarget, thread.Types, thread.Globals);
var target = new TestPlaceholderTarget(arch, thread.Builder.GetMemoryContext().ReadFromTarget, thread.Types, thread.Globals);
target.SetContracts(Mock.Of<ContractRegistry>(
c => c.Thread == ((IContractFactory<IThread>)new ThreadFactory()).CreateContract(target, 1)));
return target;

View File

@ -194,7 +194,7 @@ public class TypeDescTests
private static Target CreateTarget(MockDescriptors.RuntimeTypeSystem rtsBuilder)
{
var target = new TestPlaceholderTarget(rtsBuilder.Builder.TargetTestHelpers.Arch, rtsBuilder.Builder.GetReadContext().ReadFromTarget, rtsBuilder.Types, rtsBuilder.Globals);
var target = new TestPlaceholderTarget(rtsBuilder.Builder.TargetTestHelpers.Arch, rtsBuilder.Builder.GetMemoryContext().ReadFromTarget, rtsBuilder.Types, rtsBuilder.Globals);
target.SetContracts(Mock.Of<ContractRegistry>(
c => c.RuntimeTypeSystem == ((IContractFactory<IRuntimeTypeSystem>)new RuntimeTypeSystemFactory()).CreateContract(target, 1)));
return target;

View File

@ -493,6 +493,7 @@ public static class Program
GetDescriptor(contractVersion),
[TargetPointer.Null, new TargetPointer(header->memoryBase + (nuint)((byte*)&header->moduleTable - (byte*)header))],
(address, buffer) => ReadFromMemoryMappedLog(address, buffer, header),
(address, buffer) => throw new NotImplementedException("StressLogAnalyzer does not provide WriteToTarget implementation"),
(threadId, contextFlags, bufferToFill) => throw new NotImplementedException("StressLogAnalyzer does not provide GetTargetThreadContext implementation"),
true,
nuint.Size);