aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Tests/Memory
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /Ryujinx.Tests/Memory
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'Ryujinx.Tests/Memory')
-rw-r--r--Ryujinx.Tests/Memory/MockMemoryManager.cs53
-rw-r--r--Ryujinx.Tests/Memory/PartialUnmaps.cs469
2 files changed, 0 insertions, 522 deletions
diff --git a/Ryujinx.Tests/Memory/MockMemoryManager.cs b/Ryujinx.Tests/Memory/MockMemoryManager.cs
deleted file mode 100644
index eeecf419..00000000
--- a/Ryujinx.Tests/Memory/MockMemoryManager.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using ARMeilleure.Memory;
-using System;
-
-namespace Ryujinx.Tests.Memory
-{
- internal class MockMemoryManager : IMemoryManager
- {
- public int AddressSpaceBits => throw new NotImplementedException();
-
- public IntPtr PageTablePointer => throw new NotImplementedException();
-
- public MemoryManagerType Type => MemoryManagerType.HostMappedUnsafe;
-
-#pragma warning disable CS0067
- public event Action<ulong, ulong> UnmapEvent;
-#pragma warning restore CS0067
-
- public ref T GetRef<T>(ulong va) where T : unmanaged
- {
- throw new NotImplementedException();
- }
-
- public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
- {
- throw new NotImplementedException();
- }
-
- public bool IsMapped(ulong va)
- {
- throw new NotImplementedException();
- }
-
- public T Read<T>(ulong va) where T : unmanaged
- {
- throw new NotImplementedException();
- }
-
- public T ReadTracked<T>(ulong va) where T : unmanaged
- {
- throw new NotImplementedException();
- }
-
- public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
- {
- throw new NotImplementedException();
- }
-
- public void Write<T>(ulong va, T value) where T : unmanaged
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/Ryujinx.Tests/Memory/PartialUnmaps.cs b/Ryujinx.Tests/Memory/PartialUnmaps.cs
deleted file mode 100644
index b805969d..00000000
--- a/Ryujinx.Tests/Memory/PartialUnmaps.cs
+++ /dev/null
@@ -1,469 +0,0 @@
-using ARMeilleure.Signal;
-using ARMeilleure.Translation;
-using NUnit.Framework;
-using Ryujinx.Common.Memory.PartialUnmaps;
-using Ryujinx.Cpu;
-using Ryujinx.Cpu.Jit;
-using Ryujinx.Memory;
-using Ryujinx.Memory.Tests;
-using Ryujinx.Memory.Tracking;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace Ryujinx.Tests.Memory
-{
- [TestFixture]
- internal class PartialUnmaps
- {
- private static Translator _translator;
-
- private (MemoryBlock virt, MemoryBlock mirror, MemoryEhMeilleure exceptionHandler) GetVirtual(ulong asSize)
- {
- MemoryAllocationFlags asFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible;
-
- var addressSpace = new MemoryBlock(asSize, asFlags);
- var addressSpaceMirror = new MemoryBlock(asSize, asFlags);
-
- var tracking = new MemoryTracking(new MockVirtualMemoryManager(asSize, 0x1000), 0x1000);
- var exceptionHandler = new MemoryEhMeilleure(addressSpace, addressSpaceMirror, tracking);
-
- return (addressSpace, addressSpaceMirror, exceptionHandler);
- }
-
- private int CountThreads(ref PartialUnmapState state)
- {
- int count = 0;
-
- ref var ids = ref state.LocalCounts.ThreadIds;
-
- for (int i = 0; i < ids.Length; i++)
- {
- if (ids[i] != 0)
- {
- count++;
- }
- }
-
- return count;
- }
-
- private void EnsureTranslator()
- {
- // Create a translator, as one is needed to register the signal handler or emit methods.
- _translator ??= new Translator(new JitMemoryAllocator(), new MockMemoryManager(), true);
- }
-
- [Test]
- // Memory aliasing tests fail on CI at the moment.
- [Platform(Exclude = "MacOsX")]
- public void PartialUnmap([Values] bool readOnly)
- {
- // Set up an address space to test partial unmapping.
- // Should register the signal handler to deal with this on Windows.
- ulong vaSize = 0x100000;
-
- // The first 0x100000 is mapped to start. It is replaced from the center with the 0x200000 mapping.
- var backing = new MemoryBlock(vaSize * 2, MemoryAllocationFlags.Mirrorable);
-
- (MemoryBlock unusedMainMemory, MemoryBlock memory, MemoryEhMeilleure exceptionHandler) = GetVirtual(vaSize * 2);
-
- EnsureTranslator();
-
- ref var state = ref PartialUnmapState.GetRef();
-
- Thread testThread = null;
- bool shouldAccess = true;
-
- try
- {
- // Globally reset the struct for handling partial unmap races.
- PartialUnmapState.Reset();
- bool error = false;
-
- // Create a large mapping.
- memory.MapView(backing, 0, 0, vaSize);
-
- if (readOnly)
- {
- memory.Reprotect(0, vaSize, MemoryPermission.Read);
- }
-
- if (readOnly)
- {
- // Write a value to the physical memory, then try to read it repeately from virtual.
- // It should not change.
- testThread = new Thread(() =>
- {
- int i = 12345;
- backing.Write(vaSize - 0x1000, i);
-
- while (shouldAccess)
- {
- if (memory.Read<int>(vaSize - 0x1000) != i)
- {
- error = true;
- shouldAccess = false;
- }
- }
- });
- }
- else
- {
- // Repeatedly write and check the value on the last page of the mapping on another thread.
- testThread = new Thread(() =>
- {
- int i = 0;
- while (shouldAccess)
- {
- memory.Write(vaSize - 0x1000, i);
- if (memory.Read<int>(vaSize - 0x1000) != i)
- {
- error = true;
- shouldAccess = false;
- }
-
- i++;
- }
- });
- }
-
- testThread.Start();
-
- // Create a smaller mapping, covering the larger mapping.
- // Immediately try to write to the part of the larger mapping that did not change.
- // Do this a lot, with the smaller mapping gradually increasing in size. Should not crash, data should not be lost.
-
- ulong pageSize = 0x1000;
- int mappingExpandCount = (int)(vaSize / (pageSize * 2)) - 1;
- ulong vaCenter = vaSize / 2;
-
- for (int i = 1; i <= mappingExpandCount; i++)
- {
- ulong start = vaCenter - (pageSize * (ulong)i);
- ulong size = pageSize * (ulong)i * 2;
-
- ulong startPa = start + vaSize;
-
- memory.MapView(backing, startPa, start, size);
- }
-
- // On Windows, this should put unmap counts on the thread local map.
- if (OperatingSystem.IsWindows())
- {
- // One thread should be present on the thread local map. Trimming should remove it.
- Assert.AreEqual(1, CountThreads(ref state));
- }
-
- shouldAccess = false;
- testThread.Join();
-
- Assert.False(error);
-
- string test = null;
-
- try
- {
- test.IndexOf('1');
- }
- catch (NullReferenceException)
- {
- // This shouldn't freeze.
- }
-
- if (OperatingSystem.IsWindows())
- {
- state.TrimThreads();
-
- Assert.AreEqual(0, CountThreads(ref state));
- }
-
- /*
- * Use this to test invalid access. Can't put this in the test suite unfortunately as invalid access crashes the test process.
- * memory.Reprotect(vaSize - 0x1000, 0x1000, MemoryPermission.None);
- * //memory.UnmapView(backing, vaSize - 0x1000, 0x1000);
- * memory.Read<int>(vaSize - 0x1000);
- */
- }
- finally
- {
- // In case something failed, we want to ensure the test thread is dead before disposing of the memory.
- shouldAccess = false;
- testThread?.Join();
-
- exceptionHandler.Dispose();
- unusedMainMemory.Dispose();
- memory.Dispose();
- backing.Dispose();
- }
- }
-
- [Test]
- // Memory aliasing tests fail on CI at the moment.
- [Platform(Exclude = "MacOsX")]
- public unsafe void PartialUnmapNative()
- {
-
- // Set up an address space to test partial unmapping.
- // Should register the signal handler to deal with this on Windows.
- ulong vaSize = 0x100000;
-
- // The first 0x100000 is mapped to start. It is replaced from the center with the 0x200000 mapping.
- var backing = new MemoryBlock(vaSize * 2, MemoryAllocationFlags.Mirrorable);
-
- (MemoryBlock mainMemory, MemoryBlock unusedMirror, MemoryEhMeilleure exceptionHandler) = GetVirtual(vaSize * 2);
-
- EnsureTranslator();
-
- ref var state = ref PartialUnmapState.GetRef();
-
- // Create some state to be used for managing the native writing loop.
- int stateSize = Unsafe.SizeOf<NativeWriteLoopState>();
- var statePtr = Marshal.AllocHGlobal(stateSize);
- Unsafe.InitBlockUnaligned((void*)statePtr, 0, (uint)stateSize);
-
- ref NativeWriteLoopState writeLoopState = ref Unsafe.AsRef<NativeWriteLoopState>((void*)statePtr);
- writeLoopState.Running = 1;
- writeLoopState.Error = 0;
-
- try
- {
- // Globally reset the struct for handling partial unmap races.
- PartialUnmapState.Reset();
-
- // Create a large mapping.
- mainMemory.MapView(backing, 0, 0, vaSize);
-
- var writeFunc = TestMethods.GenerateDebugNativeWriteLoop();
- IntPtr writePtr = mainMemory.GetPointer(vaSize - 0x1000, 4);
-
- Thread testThread = new Thread(() =>
- {
- writeFunc(statePtr, writePtr);
- });
-
- testThread.Start();
-
- // Create a smaller mapping, covering the larger mapping.
- // Immediately try to write to the part of the larger mapping that did not change.
- // Do this a lot, with the smaller mapping gradually increasing in size. Should not crash, data should not be lost.
-
- ulong pageSize = 0x1000;
- int mappingExpandCount = (int)(vaSize / (pageSize * 2)) - 1;
- ulong vaCenter = vaSize / 2;
-
- for (int i = 1; i <= mappingExpandCount; i++)
- {
- ulong start = vaCenter - (pageSize * (ulong)i);
- ulong size = pageSize * (ulong)i * 2;
-
- ulong startPa = start + vaSize;
-
- mainMemory.MapView(backing, startPa, start, size);
- }
-
- writeLoopState.Running = 0;
- testThread.Join();
-
- Assert.False(writeLoopState.Error != 0);
- }
- finally
- {
- Marshal.FreeHGlobal(statePtr);
-
- exceptionHandler.Dispose();
- mainMemory.Dispose();
- unusedMirror.Dispose();
- backing.Dispose();
- }
- }
-
- [Test]
- // Only test in Windows, as this is only used on Windows and uses Windows APIs for trimming.
- [Platform("Win")]
- [SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")]
- public void ThreadLocalMap()
- {
- PartialUnmapState.Reset();
- ref var state = ref PartialUnmapState.GetRef();
-
- bool running = true;
- var testThread = new Thread(() =>
- {
- PartialUnmapState.GetRef().RetryFromAccessViolation();
- while (running)
- {
- Thread.Sleep(1);
- }
- });
-
- testThread.Start();
- Thread.Sleep(200);
-
- Assert.AreEqual(1, CountThreads(ref state));
-
- // Trimming should not remove the thread as it's still active.
- state.TrimThreads();
- Assert.AreEqual(1, CountThreads(ref state));
-
- running = false;
-
- testThread.Join();
-
- // Should trim now that it's inactive.
- state.TrimThreads();
- Assert.AreEqual(0, CountThreads(ref state));
- }
-
- [Test]
- // Only test in Windows, as this is only used on Windows and uses Windows APIs for trimming.
- [Platform("Win")]
- public unsafe void ThreadLocalMapNative()
- {
- EnsureTranslator();
-
- PartialUnmapState.Reset();
-
- ref var state = ref PartialUnmapState.GetRef();
-
- fixed (void* localMap = &state.LocalCounts)
- {
- var getOrReserve = TestMethods.GenerateDebugThreadLocalMapGetOrReserve((IntPtr)localMap);
-
- for (int i = 0; i < ThreadLocalMap<int>.MapSize; i++)
- {
- // Should obtain the index matching the call #.
- Assert.AreEqual(i, getOrReserve(i + 1, i));
-
- // Check that this and all previously reserved thread IDs and struct contents are intact.
- for (int j = 0; j <= i; j++)
- {
- Assert.AreEqual(j + 1, state.LocalCounts.ThreadIds[j]);
- Assert.AreEqual(j, state.LocalCounts.Structs[j]);
- }
- }
-
- // Trying to reserve again when the map is full should return -1.
- Assert.AreEqual(-1, getOrReserve(200, 0));
-
- for (int i = 0; i < ThreadLocalMap<int>.MapSize; i++)
- {
- // Should obtain the index matching the call #, as it already exists.
- Assert.AreEqual(i, getOrReserve(i + 1, -1));
-
- // The struct should not be reset to -1.
- Assert.AreEqual(i, state.LocalCounts.Structs[i]);
- }
-
- // Clear one of the ids as if it were freed.
- state.LocalCounts.ThreadIds[13] = 0;
-
- // GetOrReserve should now obtain and return 13.
- Assert.AreEqual(13, getOrReserve(300, 301));
- Assert.AreEqual(300, state.LocalCounts.ThreadIds[13]);
- Assert.AreEqual(301, state.LocalCounts.Structs[13]);
- }
- }
-
- [Test]
- public void NativeReaderWriterLock()
- {
- var rwLock = new NativeReaderWriterLock();
- var threads = new List<Thread>();
-
- int value = 0;
-
- bool running = true;
- bool error = false;
- int readersAllowed = 1;
-
- for (int i = 0; i < 5; i++)
- {
- var readThread = new Thread(() =>
- {
- int count = 0;
- while (running)
- {
- rwLock.AcquireReaderLock();
-
- int originalValue = Thread.VolatileRead(ref value);
-
- count++;
-
- // Spin a bit.
- for (int i = 0; i < 100; i++)
- {
- if (Thread.VolatileRead(ref readersAllowed) == 0)
- {
- error = true;
- running = false;
- }
- }
-
- // Should not change while the lock is held.
- if (Thread.VolatileRead(ref value) != originalValue)
- {
- error = true;
- running = false;
- }
-
- rwLock.ReleaseReaderLock();
- }
- });
-
- threads.Add(readThread);
- }
-
- for (int i = 0; i < 2; i++)
- {
- var writeThread = new Thread(() =>
- {
- int count = 0;
- while (running)
- {
- rwLock.AcquireReaderLock();
- rwLock.UpgradeToWriterLock();
-
- Thread.Sleep(2);
- count++;
-
- Interlocked.Exchange(ref readersAllowed, 0);
-
- for (int i = 0; i < 10; i++)
- {
- Interlocked.Increment(ref value);
- }
-
- Interlocked.Exchange(ref readersAllowed, 1);
-
- rwLock.DowngradeFromWriterLock();
- rwLock.ReleaseReaderLock();
-
- Thread.Sleep(1);
- }
- });
-
- threads.Add(writeThread);
- }
-
- foreach (var thread in threads)
- {
- thread.Start();
- }
-
- Thread.Sleep(1000);
-
- running = false;
-
- foreach (var thread in threads)
- {
- thread.Join();
- }
-
- Assert.False(error);
- }
- }
-} \ No newline at end of file