mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
121 lines
5.4 KiB
C#
121 lines
5.4 KiB
C#
using System.Text.Json.Nodes;
|
|
|
|
namespace Penumbra.CrashHandler.Buffers;
|
|
|
|
/// <summary> The types of currently hooked and relevant animation loading functions. </summary>
|
|
public enum AnimationInvocationType : int
|
|
{
|
|
PapLoad,
|
|
ActionLoad,
|
|
ScheduleClipUpdate,
|
|
LoadTimelineResources,
|
|
LoadCharacterVfx,
|
|
LoadCharacterSound,
|
|
ApricotSoundPlay,
|
|
LoadAreaVfx,
|
|
CharacterBaseLoadAnimation,
|
|
}
|
|
|
|
/// <summary> The full crash entry for an invoked vfx function. </summary>
|
|
public record struct VfxFuncInvokedEntry(
|
|
double Age,
|
|
DateTimeOffset Timestamp,
|
|
int ThreadId,
|
|
string InvocationType,
|
|
string CharacterName,
|
|
string CharacterAddress,
|
|
Guid CollectionId) : ICrashDataEntry;
|
|
|
|
/// <summary> Only expose the write interface for the buffer. </summary>
|
|
public interface IAnimationInvocationBufferWriter
|
|
{
|
|
/// <summary> Write a line into the buffer with the given data. </summary>
|
|
/// <param name="characterAddress"> The address of the related character, if known. </param>
|
|
/// <param name="characterName"> The name of the related character, anonymized or relying on index if unavailable, if known. </param>
|
|
/// <param name="collectionId"> The GUID of the associated collection. </param>
|
|
/// <param name="type"> The type of VFX func called. </param>
|
|
public void WriteLine(nint characterAddress, ReadOnlySpan<byte> characterName, Guid collectionId, AnimationInvocationType type);
|
|
}
|
|
|
|
internal sealed class AnimationInvocationBuffer : MemoryMappedBuffer, IAnimationInvocationBufferWriter, IBufferReader
|
|
{
|
|
private const int _version = 1;
|
|
private const int _lineCount = 64;
|
|
private const int _lineCapacity = 128;
|
|
private const string _name = "Penumbra.AnimationInvocation";
|
|
|
|
public void WriteLine(nint characterAddress, ReadOnlySpan<byte> characterName, Guid collectionId, AnimationInvocationType type)
|
|
{
|
|
var accessor = GetCurrentLineLocking();
|
|
lock (accessor)
|
|
{
|
|
accessor.Write(0, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
|
|
accessor.Write(8, Environment.CurrentManagedThreadId);
|
|
accessor.Write(12, (int)type);
|
|
accessor.Write(16, characterAddress);
|
|
var span = GetSpan(accessor, 24, 16);
|
|
collectionId.TryWriteBytes(span);
|
|
accessor.SafeMemoryMappedViewHandle.ReleasePointer();
|
|
span = GetSpan(accessor, 40);
|
|
WriteSpan(characterName, span);
|
|
accessor.SafeMemoryMappedViewHandle.ReleasePointer();
|
|
}
|
|
}
|
|
|
|
public uint TotalCount
|
|
=> TotalWrittenLines;
|
|
|
|
public IEnumerable<JsonObject> GetLines(DateTimeOffset crashTime)
|
|
{
|
|
var lineCount = (int)CurrentLineCount;
|
|
for (var i = lineCount - 1; i >= 0; --i)
|
|
{
|
|
var line = GetLine(i);
|
|
var timestamp = DateTimeOffset.FromUnixTimeMilliseconds(BitConverter.ToInt64(line));
|
|
var thread = BitConverter.ToInt32(line[8..]);
|
|
var type = (AnimationInvocationType)BitConverter.ToInt32(line[12..]);
|
|
var address = BitConverter.ToUInt64(line[16..]);
|
|
var collectionId = new Guid(line[24..40]);
|
|
var characterName = ReadString(line[40..]);
|
|
yield return new JsonObject()
|
|
{
|
|
[nameof(VfxFuncInvokedEntry.Age)] = (crashTime - timestamp).TotalSeconds,
|
|
[nameof(VfxFuncInvokedEntry.Timestamp)] = timestamp,
|
|
[nameof(VfxFuncInvokedEntry.ThreadId)] = thread,
|
|
[nameof(VfxFuncInvokedEntry.InvocationType)] = ToName(type),
|
|
[nameof(VfxFuncInvokedEntry.CharacterName)] = characterName,
|
|
[nameof(VfxFuncInvokedEntry.CharacterAddress)] = address.ToString("X"),
|
|
[nameof(VfxFuncInvokedEntry.CollectionId)] = collectionId,
|
|
};
|
|
}
|
|
}
|
|
|
|
public static IBufferReader CreateReader(int pid)
|
|
=> new AnimationInvocationBuffer(false, pid);
|
|
|
|
public static IAnimationInvocationBufferWriter CreateWriter(int pid)
|
|
=> new AnimationInvocationBuffer(pid);
|
|
|
|
private AnimationInvocationBuffer(bool writer, int pid)
|
|
: base($"{_name}_{pid}_{_version}", _version)
|
|
{ }
|
|
|
|
private AnimationInvocationBuffer(int pid)
|
|
: base($"{_name}_{pid}_{_version}", _version, _lineCount, _lineCapacity)
|
|
{ }
|
|
|
|
private static string ToName(AnimationInvocationType type)
|
|
=> type switch
|
|
{
|
|
AnimationInvocationType.PapLoad => "PAP Load",
|
|
AnimationInvocationType.ActionLoad => "Action Load",
|
|
AnimationInvocationType.ScheduleClipUpdate => "Schedule Clip Update",
|
|
AnimationInvocationType.LoadTimelineResources => "Load Timeline Resources",
|
|
AnimationInvocationType.LoadCharacterVfx => "Load Character VFX",
|
|
AnimationInvocationType.LoadCharacterSound => "Load Character Sound",
|
|
AnimationInvocationType.ApricotSoundPlay => "Apricot Sound Play",
|
|
AnimationInvocationType.LoadAreaVfx => "Load Area VFX",
|
|
AnimationInvocationType.CharacterBaseLoadAnimation => "Load Animation (CharacterBase)",
|
|
_ => $"Unknown ({(int)type})",
|
|
};
|
|
}
|