[Diagnostics] Summarize synchronized table reads and trace several writer addresses
SHARPEMU_TRACE_GPU_READS=1 now also reports, every five seconds, the guest words resource planning read through a GPU synchronization: read count, how often the value changed, and the first and last value. SHARPEMU_TRACE_WRITER_ADDRESS accepts a comma-separated list.
This commit is contained in:
parent
ca102a67c7
commit
5f26576d6b
|
|
@ -9,15 +9,19 @@ namespace SharpEmu.Libs.Diagnostics;
|
||||||
|
|
||||||
internal static class DccWriterTrace
|
internal static class DccWriterTrace
|
||||||
{
|
{
|
||||||
private const int MaxReports = 64;
|
private const int MaxReports = 256;
|
||||||
private const int MaxFrames = 12;
|
private const int MaxFrames = 12;
|
||||||
|
|
||||||
private static readonly bool DccEnabled =
|
private static readonly bool DccEnabled =
|
||||||
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_DCC_WRITERS") == "1";
|
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_DCC_WRITERS") == "1";
|
||||||
|
|
||||||
private static readonly ulong TargetAddress = ParseAddress(Environment.GetEnvironmentVariable("SHARPEMU_TRACE_WRITER_ADDRESS"));
|
private static readonly ulong[] TargetAddresses = (Environment.GetEnvironmentVariable("SHARPEMU_TRACE_WRITER_ADDRESS") ?? string.Empty)
|
||||||
|
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||||
|
.Select(ParseAddress)
|
||||||
|
.Where(address => address != 0)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
public static readonly bool Enabled = DccEnabled || TargetAddress != 0;
|
public static readonly bool Enabled = DccEnabled || TargetAddresses.Length != 0;
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
public static ulong CurrentProgram;
|
public static ulong CurrentProgram;
|
||||||
|
|
@ -43,7 +47,7 @@ internal static class DccWriterTrace
|
||||||
|
|
||||||
public static void Record(IGuestImageCache images, ulong address, ulong size)
|
public static void Record(IGuestImageCache images, ulong address, ulong size)
|
||||||
{
|
{
|
||||||
var targeted = TargetAddress != 0 && TargetAddress >= address && TargetAddress - address < size;
|
var targeted = TargetAddresses.Any(target => target >= address && target - address < size);
|
||||||
if (_reported.Count >= MaxReports || (!targeted && !(DccEnabled && images.OverlapsDccMetadata(address, size))) ||
|
if (_reported.Count >= MaxReports || (!targeted && !(DccEnabled && images.OverlapsDccMetadata(address, size))) ||
|
||||||
!_reported.Add((address, size, CurrentProgram)))
|
!_reported.Add((address, size, CurrentProgram)))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,30 @@ internal static class GpuReadTrace
|
||||||
public static string? CurrentStage;
|
public static string? CurrentStage;
|
||||||
|
|
||||||
private static readonly HashSet<(ulong Shader, ulong Page, bool Table)> _reported = new();
|
private static readonly HashSet<(ulong Shader, ulong Page, bool Table)> _reported = new();
|
||||||
|
private static readonly Dictionary<ulong, (long Reads, long Changes, uint Last, uint First, ulong Shader)> _values = new();
|
||||||
|
private static long _nextValueReport = System.Diagnostics.Stopwatch.GetTimestamp();
|
||||||
|
|
||||||
|
public static void RecordValue(ulong address, uint word)
|
||||||
|
{
|
||||||
|
lock (_values)
|
||||||
|
{
|
||||||
|
_values[address] = _values.TryGetValue(address, out var entry)
|
||||||
|
? (entry.Reads + 1, entry.Changes + (entry.Last != word ? 1 : 0), word, entry.First, entry.Shader)
|
||||||
|
: (1, 0, word, word, CurrentShader);
|
||||||
|
var now = System.Diagnostics.Stopwatch.GetTimestamp();
|
||||||
|
if (now < _nextValueReport)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_nextValueReport = now + System.Diagnostics.Stopwatch.Frequency * 5;
|
||||||
|
foreach (var (key, value) in _values.OrderByDescending(pair => pair.Value.Reads).Take(16))
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[GPU][SYNC_READ_VALUE] address=0x{key:X16} reads={value.Reads} changes={value.Changes} first=0x{value.First:X8} last=0x{value.Last:X8} shader=0x{value.Shader:X16}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Record(ulong address, bool table)
|
public static void Record(ulong address, bool table)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ internal static unsafe partial class VulkanVideoPresenter
|
||||||
{
|
{
|
||||||
using var profile = ResourceMaterializationProfile.Measure(ResourceMaterializationProfile.Phase.GuestRead);
|
using var profile = ResourceMaterializationProfile.Measure(ResourceMaterializationProfile.Phase.GuestRead);
|
||||||
word = 0;
|
word = 0;
|
||||||
|
var synchronized = false;
|
||||||
if (IsCleanReadPage(address, sizeof(uint)))
|
if (IsCleanReadPage(address, sizeof(uint)))
|
||||||
{
|
{
|
||||||
if (TryGetAliasPointer(address, sizeof(uint), out var alias))
|
if (TryGetAliasPointer(address, sizeof(uint), out var alias))
|
||||||
|
|
@ -132,6 +133,8 @@ internal static unsafe partial class VulkanVideoPresenter
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -146,6 +149,11 @@ internal static unsafe partial class VulkanVideoPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
word = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(bytes);
|
word = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(bytes);
|
||||||
|
if (Diagnostics.GpuReadTrace.Enabled && synchronized)
|
||||||
|
{
|
||||||
|
Diagnostics.GpuReadTrace.RecordValue(address, word);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue