Optimized memory modified check (#538)

* Optimized memory modified check

This was initially in some cases more expensive than plainly sending the data. Now it should have way better performance.

* Small refactoring

* renamed InvalidAccessEventArgs
* Renamed PtPageBits

* Removed ValueRange(set)

They are currently unused and won't be likely to be used in the near future
This commit is contained in:
Roderick Sieben 2018-12-12 02:48:54 +01:00 committed by gdkchan
parent 01d9716d20
commit 63ae8679a3
2 changed files with 25 additions and 52 deletions

View file

@ -2,11 +2,11 @@ using System;
namespace ChocolArm64.Events namespace ChocolArm64.Events
{ {
public class InvalidAccessEventArgs : EventArgs public class MemoryAccessEventArgs : EventArgs
{ {
public long Position { get; private set; } public long Position { get; private set; }
public InvalidAccessEventArgs(long position) public MemoryAccessEventArgs(long position)
{ {
Position = position; Position = position;
} }

View file

@ -17,18 +17,18 @@ namespace ChocolArm64.Memory
{ {
private const int PtLvl0Bits = 13; private const int PtLvl0Bits = 13;
private const int PtLvl1Bits = 14; private const int PtLvl1Bits = 14;
private const int PtPageBits = 12; public const int PageBits = 12;
private const int PtLvl0Size = 1 << PtLvl0Bits; private const int PtLvl0Size = 1 << PtLvl0Bits;
private const int PtLvl1Size = 1 << PtLvl1Bits; private const int PtLvl1Size = 1 << PtLvl1Bits;
public const int PageSize = 1 << PtPageBits; public const int PageSize = 1 << PageBits;
private const int PtLvl0Mask = PtLvl0Size - 1; private const int PtLvl0Mask = PtLvl0Size - 1;
private const int PtLvl1Mask = PtLvl1Size - 1; private const int PtLvl1Mask = PtLvl1Size - 1;
public const int PageMask = PageSize - 1; public const int PageMask = PageSize - 1;
private const int PtLvl0Bit = PtPageBits + PtLvl1Bits; private const int PtLvl0Bit = PageBits + PtLvl1Bits;
private const int PtLvl1Bit = PtPageBits; private const int PtLvl1Bit = PageBits;
private const long ErgMask = (4 << CpuThreadState.ErgSizeLog2) - 1; private const long ErgMask = (4 << CpuThreadState.ErgSizeLog2) - 1;
@ -53,7 +53,9 @@ namespace ChocolArm64.Memory
private byte*** _pageTable; private byte*** _pageTable;
public event EventHandler<InvalidAccessEventArgs> InvalidAccess; public event EventHandler<MemoryAccessEventArgs> InvalidAccess;
public event EventHandler<MemoryAccessEventArgs> ObservedAccess;
public MemoryManager(IntPtr ram) public MemoryManager(IntPtr ram)
{ {
@ -632,7 +634,7 @@ namespace ChocolArm64.Memory
return false; return false;
} }
return _pageTable[l0][l1] != null || _observedPages.ContainsKey(position >> PtPageBits); return _pageTable[l0][l1] != null || _observedPages.ContainsKey(position >> PageBits);
} }
public long GetPhysicalAddress(long virtualAddress) public long GetPhysicalAddress(long virtualAddress)
@ -678,14 +680,14 @@ Unmapped:
private byte* HandleNullPte(long position) private byte* HandleNullPte(long position)
{ {
long key = position >> PtPageBits; long key = position >> PageBits;
if (_observedPages.TryGetValue(key, out IntPtr ptr)) if (_observedPages.TryGetValue(key, out IntPtr ptr))
{ {
return (byte*)ptr + (position & PageMask); return (byte*)ptr + (position & PageMask);
} }
InvalidAccess?.Invoke(this, new InvalidAccessEventArgs(position)); InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position));
throw new VmmPageFaultException(position); throw new VmmPageFaultException(position);
} }
@ -726,16 +728,20 @@ Unmapped:
private byte* HandleNullPteWrite(long position) private byte* HandleNullPteWrite(long position)
{ {
long key = position >> PtPageBits; long key = position >> PageBits;
MemoryAccessEventArgs e = new MemoryAccessEventArgs(position);
if (_observedPages.TryGetValue(key, out IntPtr ptr)) if (_observedPages.TryGetValue(key, out IntPtr ptr))
{ {
SetPtEntry(position, (byte*)ptr); SetPtEntry(position, (byte*)ptr);
ObservedAccess?.Invoke(this, e);
return (byte*)ptr + (position & PageMask); return (byte*)ptr + (position & PageMask);
} }
InvalidAccess?.Invoke(this, new InvalidAccessEventArgs(position)); InvalidAccess?.Invoke(this, e);
throw new VmmPageFaultException(position); throw new VmmPageFaultException(position);
} }
@ -784,45 +790,15 @@ Unmapped:
_pageTable[l0][l1] = ptr; _pageTable[l0][l1] = ptr;
} }
public (bool[], int) IsRegionModified(long position, long size) public void StartObservingRegion(long position, long size)
{ {
long endPosition = (position + size + PageMask) & ~PageMask; long endPosition = (position + size + PageMask) & ~PageMask;
position &= ~PageMask; position &= ~PageMask;
size = endPosition - position; while ((ulong)position < (ulong)endPosition)
bool[] modified = new bool[size >> PtPageBits];
int count = 0;
lock (_observedPages)
{ {
for (int page = 0; page < modified.Length; page++) _observedPages[position >> PageBits] = (IntPtr)Translate(position);
{
byte* ptr = Translate(position);
if (_observedPages.TryAdd(position >> PtPageBits, (IntPtr)ptr))
{
modified[page] = true;
count++;
}
else
{
long l0 = (position >> PtLvl0Bit) & PtLvl0Mask;
long l1 = (position >> PtLvl1Bit) & PtLvl1Mask;
byte** lvl1 = _pageTable[l0];
if (lvl1 != null)
{
if (modified[page] = lvl1[l1] != null)
{
count++;
}
}
}
SetPtEntry(position, null); SetPtEntry(position, null);
@ -830,9 +806,6 @@ Unmapped:
} }
} }
return (modified, count);
}
public void StopObservingRegion(long position, long size) public void StopObservingRegion(long position, long size)
{ {
long endPosition = (position + size + PageMask) & ~PageMask; long endPosition = (position + size + PageMask) & ~PageMask;
@ -841,7 +814,7 @@ Unmapped:
{ {
lock (_observedPages) lock (_observedPages)
{ {
if (_observedPages.TryRemove(position >> PtPageBits, out IntPtr ptr)) if (_observedPages.TryRemove(position >> PageBits, out IntPtr ptr))
{ {
SetPtEntry(position, (byte*)ptr); SetPtEntry(position, (byte*)ptr);
} }
@ -891,7 +864,7 @@ Unmapped:
public bool IsValidPosition(long position) public bool IsValidPosition(long position)
{ {
return position >> (PtLvl0Bits + PtLvl1Bits + PtPageBits) == 0; return position >> (PtLvl0Bits + PtLvl1Bits + PageBits) == 0;
} }
public void Dispose() public void Dispose()