Make crashhandler somewhat more stable, support multi boxing maybe?

This commit is contained in:
Ottermandias 2024-03-21 20:27:55 +01:00
parent c55d6966cd
commit f3ceb9034e
8 changed files with 105 additions and 61 deletions

View file

@ -88,18 +88,18 @@ internal sealed class AnimationInvocationBuffer : MemoryMappedBuffer, IAnimation
} }
} }
public static IBufferReader CreateReader() public static IBufferReader CreateReader(int pid)
=> new AnimationInvocationBuffer(false); => new AnimationInvocationBuffer(false, pid);
public static IAnimationInvocationBufferWriter CreateWriter() public static IAnimationInvocationBufferWriter CreateWriter(int pid)
=> new AnimationInvocationBuffer(); => new AnimationInvocationBuffer(pid);
private AnimationInvocationBuffer(bool writer) private AnimationInvocationBuffer(bool writer, int pid)
: base(_name, _version) : base($"{_name}_{pid}_{_version}", _version)
{ } { }
private AnimationInvocationBuffer() private AnimationInvocationBuffer(int pid)
: base(_name, _version, _lineCount, _lineCapacity) : base($"{_name}_{pid}_{_version}", _version, _lineCount, _lineCapacity)
{ } { }
private static string ToName(AnimationInvocationType type) private static string ToName(AnimationInvocationType type)

View file

@ -69,17 +69,17 @@ internal sealed class CharacterBaseBuffer : MemoryMappedBuffer, ICharacterBaseBu
public uint TotalCount public uint TotalCount
=> TotalWrittenLines; => TotalWrittenLines;
public static IBufferReader CreateReader() public static IBufferReader CreateReader(int pid)
=> new CharacterBaseBuffer(false); => new CharacterBaseBuffer(false, pid);
public static ICharacterBaseBufferWriter CreateWriter() public static ICharacterBaseBufferWriter CreateWriter(int pid)
=> new CharacterBaseBuffer(); => new CharacterBaseBuffer(pid);
private CharacterBaseBuffer(bool writer) private CharacterBaseBuffer(bool writer, int pid)
: base(_name, _version) : base($"{_name}_{pid}_{_version}", _version)
{ } { }
private CharacterBaseBuffer() private CharacterBaseBuffer(int pid)
: base(_name, _version, _lineCount, _lineCapacity) : base($"{_name}_{pid}_{_version}", _version, _lineCount, _lineCapacity)
{ } { }
} }

View file

@ -11,7 +11,7 @@ public class MemoryMappedBuffer : IDisposable
private readonly MemoryMappedFile _file; private readonly MemoryMappedFile _file;
private readonly MemoryMappedViewAccessor _header; private readonly MemoryMappedViewAccessor _header;
private readonly MemoryMappedViewAccessor[] _lines; private readonly MemoryMappedViewAccessor[] _lines = [];
public readonly int Version; public readonly int Version;
public readonly uint LineCount; public readonly uint LineCount;
@ -64,7 +64,8 @@ public class MemoryMappedBuffer : IDisposable
public MemoryMappedBuffer(string mapName, int? expectedVersion = null, uint? expectedMinLineCount = null, public MemoryMappedBuffer(string mapName, int? expectedVersion = null, uint? expectedMinLineCount = null,
uint? expectedMinLineCapacity = null) uint? expectedMinLineCapacity = null)
{ {
_file = MemoryMappedFile.OpenExisting(mapName, MemoryMappedFileRights.ReadWrite, HandleInheritability.Inheritable); _lines = [];
_file = MemoryMappedFile.OpenExisting(mapName, MemoryMappedFileRights.ReadWrite, HandleInheritability.Inheritable);
using var headerLine = _file.CreateViewAccessor(0, 4, MemoryMappedFileAccess.Read); using var headerLine = _file.CreateViewAccessor(0, 4, MemoryMappedFileAccess.Read);
var headerLength = headerLine.ReadUInt32(0); var headerLength = headerLine.ReadUInt32(0);
if (headerLength < MinHeaderLength) if (headerLength < MinHeaderLength)
@ -96,6 +97,7 @@ public class MemoryMappedBuffer : IDisposable
void Throw(string text) void Throw(string text)
{ {
_file.Dispose(); _file.Dispose();
_header?.Dispose();
_disposed = true; _disposed = true;
throw new Exception(text); throw new Exception(text);
} }
@ -204,10 +206,10 @@ public class MemoryMappedBuffer : IDisposable
if (_disposed) if (_disposed)
return; return;
_header.Dispose(); _header?.Dispose();
foreach (var line in _lines) foreach (var line in _lines)
line.Dispose(); line?.Dispose();
_file.Dispose(); _file?.Dispose();
} }
~MemoryMappedBuffer() ~MemoryMappedBuffer()

View file

@ -83,17 +83,17 @@ internal sealed class ModdedFileBuffer : MemoryMappedBuffer, IModdedFileBufferWr
} }
} }
public static IBufferReader CreateReader() public static IBufferReader CreateReader(int pid)
=> new ModdedFileBuffer(false); => new ModdedFileBuffer(false, pid);
public static IModdedFileBufferWriter CreateWriter() public static IModdedFileBufferWriter CreateWriter(int pid)
=> new ModdedFileBuffer(); => new ModdedFileBuffer(pid);
private ModdedFileBuffer(bool writer) private ModdedFileBuffer(bool writer, int pid)
: base(_name, _version) : base($"{_name}_{pid}_{_version}", _version)
{ } { }
private ModdedFileBuffer() private ModdedFileBuffer(int pid)
: base(_name, _version, _lineCount, _lineCapacity) : base($"{_name}_{pid}_{_version}", _version, _lineCount, _lineCapacity)
{ } { }
} }

View file

@ -9,13 +9,13 @@ public interface IBufferReader
public IEnumerable<JsonObject> GetLines(DateTimeOffset crashTime); public IEnumerable<JsonObject> GetLines(DateTimeOffset crashTime);
} }
public sealed class GameEventLogReader : IDisposable public sealed class GameEventLogReader(int pid) : IDisposable
{ {
public readonly (IBufferReader Reader, string TypeSingular, string TypePlural)[] Readers = public readonly (IBufferReader Reader, string TypeSingular, string TypePlural)[] Readers =
[ [
(CharacterBaseBuffer.CreateReader(), "CharacterLoaded", "CharactersLoaded"), (CharacterBaseBuffer.CreateReader(pid), "CharacterLoaded", "CharactersLoaded"),
(ModdedFileBuffer.CreateReader(), "ModdedFileLoaded", "ModdedFilesLoaded"), (ModdedFileBuffer.CreateReader(pid), "ModdedFileLoaded", "ModdedFilesLoaded"),
(AnimationInvocationBuffer.CreateReader(), "VFXFuncInvoked", "VFXFuncsInvoked"), (AnimationInvocationBuffer.CreateReader(pid), "VFXFuncInvoked", "VFXFuncsInvoked"),
]; ];
public void Dispose() public void Dispose()

View file

@ -2,11 +2,11 @@
namespace Penumbra.CrashHandler; namespace Penumbra.CrashHandler;
public sealed class GameEventLogWriter : IDisposable public sealed class GameEventLogWriter(int pid) : IDisposable
{ {
public readonly ICharacterBaseBufferWriter CharacterBase = CharacterBaseBuffer.CreateWriter(); public readonly ICharacterBaseBufferWriter CharacterBase = CharacterBaseBuffer.CreateWriter(pid);
public readonly IModdedFileBufferWriter FileLoaded = ModdedFileBuffer.CreateWriter(); public readonly IModdedFileBufferWriter FileLoaded = ModdedFileBuffer.CreateWriter(pid);
public readonly IAnimationInvocationBufferWriter AnimationFuncInvoked = AnimationInvocationBuffer.CreateWriter(); public readonly IAnimationInvocationBufferWriter AnimationFuncInvoked = AnimationInvocationBuffer.CreateWriter(pid);
public void Dispose() public void Dispose()
{ {

View file

@ -12,7 +12,7 @@ public class CrashHandler
try try
{ {
using var reader = new GameEventLogReader(); using var reader = new GameEventLogReader(pid);
var parent = Process.GetProcessById(pid); var parent = Process.GetProcessById(pid);
using var handle = parent.SafeHandle; using var handle = parent.SafeHandle;
parent.WaitForExit(); parent.WaitForExit();

View file

@ -24,6 +24,8 @@ public sealed class CrashHandlerService : IDisposable, IService
private readonly Configuration _config; private readonly Configuration _config;
private readonly ValidityChecker _validityChecker; private readonly ValidityChecker _validityChecker;
private string _tempExecutableDirectory = string.Empty;
public CrashHandlerService(FilenameService files, CommunicatorService communicator, ActorManager actors, ResourceLoader resourceLoader, public CrashHandlerService(FilenameService files, CommunicatorService communicator, ActorManager actors, ResourceLoader resourceLoader,
Configuration config, ValidityChecker validityChecker) Configuration config, ValidityChecker validityChecker)
{ {
@ -54,6 +56,7 @@ public sealed class CrashHandlerService : IDisposable, IService
} }
Unsubscribe(); Unsubscribe();
CleanExecutables();
} }
private Process? _child; private Process? _child;
@ -177,11 +180,12 @@ public sealed class CrashHandlerService : IDisposable, IService
try try
{ {
using var reader = new GameEventLogReader(); using var reader = new GameEventLogReader(Environment.ProcessId);
JsonObject jObj; JsonObject jObj;
lock (_eventWriter) lock (_eventWriter)
{ {
jObj = reader.Dump("Manual Dump", Environment.ProcessId, 0, $"{_validityChecker.Version} ({_validityChecker.CommitHash})", _validityChecker.GameVersion); jObj = reader.Dump("Manual Dump", Environment.ProcessId, 0, $"{_validityChecker.Version} ({_validityChecker.CommitHash})",
_validityChecker.GameVersion);
} }
var logFile = _files.LogFileName; var logFile = _files.LogFileName;
@ -198,14 +202,31 @@ public sealed class CrashHandlerService : IDisposable, IService
} }
} }
private string CopyExecutables() private void CleanExecutables()
{ {
var parent = Path.GetDirectoryName(_files.CrashHandlerExe)!; var parent = Path.GetDirectoryName(_files.CrashHandlerExe)!;
var folder = Path.Combine(parent, "temp"); foreach (var dir in Directory.EnumerateDirectories(parent, "temp_*"))
Directory.CreateDirectory(folder); {
try
{
Directory.Delete(dir, true);
}
catch (Exception ex)
{
Penumbra.Log.Debug($"Could not delete {dir}:\n{ex}");
}
}
}
private string CopyExecutables()
{
CleanExecutables();
var parent = Path.GetDirectoryName(_files.CrashHandlerExe)!;
_tempExecutableDirectory = Path.Combine(parent, $"temp_{Environment.ProcessId}");
Directory.CreateDirectory(_tempExecutableDirectory);
foreach (var file in Directory.EnumerateFiles(parent, "Penumbra.CrashHandler.*")) foreach (var file in Directory.EnumerateFiles(parent, "Penumbra.CrashHandler.*"))
File.Copy(file, Path.Combine(folder, Path.GetFileName(file)), true); File.Copy(file, Path.Combine(_tempExecutableDirectory, Path.GetFileName(file)), true);
return Path.Combine(folder, Path.GetFileName(_files.CrashHandlerExe)); return Path.Combine(_tempExecutableDirectory, Path.GetFileName(_files.CrashHandlerExe));
} }
public void LogAnimation(nint character, ModCollection collection, AnimationInvocationType type) public void LogAnimation(nint character, ModCollection collection, AnimationInvocationType type)
@ -213,10 +234,17 @@ public sealed class CrashHandlerService : IDisposable, IService
if (_eventWriter == null) if (_eventWriter == null)
return; return;
var name = GetActorName(character); try
lock (_eventWriter)
{ {
_eventWriter?.AnimationFuncInvoked.WriteLine(character, name.Span, collection.Name, type); var name = GetActorName(character);
lock (_eventWriter)
{
_eventWriter?.AnimationFuncInvoked.WriteLine(character, name.Span, collection.Name, type);
}
}
catch (Exception ex)
{
Penumbra.Log.Warning($"Error logging animation function {type} to crash handler:\n{ex}");
} }
} }
@ -225,11 +253,18 @@ public sealed class CrashHandlerService : IDisposable, IService
if (_eventWriter == null) if (_eventWriter == null)
return; return;
var name = GetActorName(address); try
lock (_eventWriter)
{ {
_eventWriter?.CharacterBase.WriteLine(address, name.Span, collection); var name = GetActorName(address);
lock (_eventWriter)
{
_eventWriter?.CharacterBase.WriteLine(address, name.Span, collection);
}
}
catch (Exception ex)
{
Penumbra.Log.Warning($"Error logging character creation to crash handler:\n{ex}");
} }
} }
@ -249,15 +284,22 @@ public sealed class CrashHandlerService : IDisposable, IService
if (manipulatedPath == null || _eventWriter == null) if (manipulatedPath == null || _eventWriter == null)
return; return;
var dashIdx = manipulatedPath.Value.InternalName[0] == (byte)'|' ? manipulatedPath.Value.InternalName.IndexOf((byte)'|', 1) : -1; try
if (dashIdx >= 0 && !Utf8GamePath.IsRooted(manipulatedPath.Value.InternalName.Substring(dashIdx + 1)))
return;
var name = GetActorName(resolveData.AssociatedGameObject);
lock (_eventWriter)
{ {
_eventWriter!.FileLoaded.WriteLine(resolveData.AssociatedGameObject, name.Span, resolveData.ModCollection.Name, var dashIdx = manipulatedPath.Value.InternalName[0] == (byte)'|' ? manipulatedPath.Value.InternalName.IndexOf((byte)'|', 1) : -1;
manipulatedPath.Value.InternalName.Span, originalPath.Path.Span); if (dashIdx >= 0 && !Utf8GamePath.IsRooted(manipulatedPath.Value.InternalName.Substring(dashIdx + 1)))
return;
var name = GetActorName(resolveData.AssociatedGameObject);
lock (_eventWriter)
{
_eventWriter!.FileLoaded.WriteLine(resolveData.AssociatedGameObject, name.Span, resolveData.ModCollection.Name,
manipulatedPath.Value.InternalName.Span, originalPath.Path.Span);
}
}
catch (Exception ex)
{
Penumbra.Log.Warning($"Error logging resource to crash handler:\n{ex}");
} }
} }
@ -276,7 +318,7 @@ public sealed class CrashHandlerService : IDisposable, IService
try try
{ {
CloseEventWriter(); CloseEventWriter();
_eventWriter = new GameEventLogWriter(); _eventWriter = new GameEventLogWriter(Environment.ProcessId);
Penumbra.Log.Debug("Opened new Event Writer for crash handler."); Penumbra.Log.Debug("Opened new Event Writer for crash handler.");
} }
catch (Exception ex) catch (Exception ex)