diff --git a/Penumbra.CrashHandler/Buffers/AnimationInvocationBuffer.cs b/Penumbra.CrashHandler/Buffers/AnimationInvocationBuffer.cs index dd966542..c92a14fd 100644 --- a/Penumbra.CrashHandler/Buffers/AnimationInvocationBuffer.cs +++ b/Penumbra.CrashHandler/Buffers/AnimationInvocationBuffer.cs @@ -88,18 +88,18 @@ internal sealed class AnimationInvocationBuffer : MemoryMappedBuffer, IAnimation } } - public static IBufferReader CreateReader() - => new AnimationInvocationBuffer(false); + public static IBufferReader CreateReader(int pid) + => new AnimationInvocationBuffer(false, pid); - public static IAnimationInvocationBufferWriter CreateWriter() - => new AnimationInvocationBuffer(); + public static IAnimationInvocationBufferWriter CreateWriter(int pid) + => new AnimationInvocationBuffer(pid); - private AnimationInvocationBuffer(bool writer) - : base(_name, _version) + private AnimationInvocationBuffer(bool writer, int pid) + : base($"{_name}_{pid}_{_version}", _version) { } - private AnimationInvocationBuffer() - : base(_name, _version, _lineCount, _lineCapacity) + private AnimationInvocationBuffer(int pid) + : base($"{_name}_{pid}_{_version}", _version, _lineCount, _lineCapacity) { } private static string ToName(AnimationInvocationType type) diff --git a/Penumbra.CrashHandler/Buffers/CharacterBaseBuffer.cs b/Penumbra.CrashHandler/Buffers/CharacterBaseBuffer.cs index 1fe5d7ba..d83c6e6c 100644 --- a/Penumbra.CrashHandler/Buffers/CharacterBaseBuffer.cs +++ b/Penumbra.CrashHandler/Buffers/CharacterBaseBuffer.cs @@ -69,17 +69,17 @@ internal sealed class CharacterBaseBuffer : MemoryMappedBuffer, ICharacterBaseBu public uint TotalCount => TotalWrittenLines; - public static IBufferReader CreateReader() - => new CharacterBaseBuffer(false); + public static IBufferReader CreateReader(int pid) + => new CharacterBaseBuffer(false, pid); - public static ICharacterBaseBufferWriter CreateWriter() - => new CharacterBaseBuffer(); + public static ICharacterBaseBufferWriter CreateWriter(int pid) + => new CharacterBaseBuffer(pid); - private CharacterBaseBuffer(bool writer) - : base(_name, _version) + private CharacterBaseBuffer(bool writer, int pid) + : base($"{_name}_{pid}_{_version}", _version) { } - private CharacterBaseBuffer() - : base(_name, _version, _lineCount, _lineCapacity) + private CharacterBaseBuffer(int pid) + : base($"{_name}_{pid}_{_version}", _version, _lineCount, _lineCapacity) { } } diff --git a/Penumbra.CrashHandler/Buffers/MemoryMappedBuffer.cs b/Penumbra.CrashHandler/Buffers/MemoryMappedBuffer.cs index c4e2627e..a1b3de52 100644 --- a/Penumbra.CrashHandler/Buffers/MemoryMappedBuffer.cs +++ b/Penumbra.CrashHandler/Buffers/MemoryMappedBuffer.cs @@ -11,7 +11,7 @@ public class MemoryMappedBuffer : IDisposable private readonly MemoryMappedFile _file; private readonly MemoryMappedViewAccessor _header; - private readonly MemoryMappedViewAccessor[] _lines; + private readonly MemoryMappedViewAccessor[] _lines = []; public readonly int Version; public readonly uint LineCount; @@ -64,7 +64,8 @@ public class MemoryMappedBuffer : IDisposable public MemoryMappedBuffer(string mapName, int? expectedVersion = null, uint? expectedMinLineCount = 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); var headerLength = headerLine.ReadUInt32(0); if (headerLength < MinHeaderLength) @@ -96,6 +97,7 @@ public class MemoryMappedBuffer : IDisposable void Throw(string text) { _file.Dispose(); + _header?.Dispose(); _disposed = true; throw new Exception(text); } @@ -204,10 +206,10 @@ public class MemoryMappedBuffer : IDisposable if (_disposed) return; - _header.Dispose(); + _header?.Dispose(); foreach (var line in _lines) - line.Dispose(); - _file.Dispose(); + line?.Dispose(); + _file?.Dispose(); } ~MemoryMappedBuffer() diff --git a/Penumbra.CrashHandler/Buffers/ModdedFileBuffer.cs b/Penumbra.CrashHandler/Buffers/ModdedFileBuffer.cs index b472d413..6c774e4b 100644 --- a/Penumbra.CrashHandler/Buffers/ModdedFileBuffer.cs +++ b/Penumbra.CrashHandler/Buffers/ModdedFileBuffer.cs @@ -83,17 +83,17 @@ internal sealed class ModdedFileBuffer : MemoryMappedBuffer, IModdedFileBufferWr } } - public static IBufferReader CreateReader() - => new ModdedFileBuffer(false); + public static IBufferReader CreateReader(int pid) + => new ModdedFileBuffer(false, pid); - public static IModdedFileBufferWriter CreateWriter() - => new ModdedFileBuffer(); + public static IModdedFileBufferWriter CreateWriter(int pid) + => new ModdedFileBuffer(pid); - private ModdedFileBuffer(bool writer) - : base(_name, _version) + private ModdedFileBuffer(bool writer, int pid) + : base($"{_name}_{pid}_{_version}", _version) { } - private ModdedFileBuffer() - : base(_name, _version, _lineCount, _lineCapacity) + private ModdedFileBuffer(int pid) + : base($"{_name}_{pid}_{_version}", _version, _lineCount, _lineCapacity) { } } diff --git a/Penumbra.CrashHandler/GameEventLogReader.cs b/Penumbra.CrashHandler/GameEventLogReader.cs index 1ae49fa5..1813a671 100644 --- a/Penumbra.CrashHandler/GameEventLogReader.cs +++ b/Penumbra.CrashHandler/GameEventLogReader.cs @@ -9,13 +9,13 @@ public interface IBufferReader public IEnumerable GetLines(DateTimeOffset crashTime); } -public sealed class GameEventLogReader : IDisposable +public sealed class GameEventLogReader(int pid) : IDisposable { public readonly (IBufferReader Reader, string TypeSingular, string TypePlural)[] Readers = [ - (CharacterBaseBuffer.CreateReader(), "CharacterLoaded", "CharactersLoaded"), - (ModdedFileBuffer.CreateReader(), "ModdedFileLoaded", "ModdedFilesLoaded"), - (AnimationInvocationBuffer.CreateReader(), "VFXFuncInvoked", "VFXFuncsInvoked"), + (CharacterBaseBuffer.CreateReader(pid), "CharacterLoaded", "CharactersLoaded"), + (ModdedFileBuffer.CreateReader(pid), "ModdedFileLoaded", "ModdedFilesLoaded"), + (AnimationInvocationBuffer.CreateReader(pid), "VFXFuncInvoked", "VFXFuncsInvoked"), ]; public void Dispose() diff --git a/Penumbra.CrashHandler/GameEventLogWriter.cs b/Penumbra.CrashHandler/GameEventLogWriter.cs index 8e809cec..e2c461f4 100644 --- a/Penumbra.CrashHandler/GameEventLogWriter.cs +++ b/Penumbra.CrashHandler/GameEventLogWriter.cs @@ -2,11 +2,11 @@ namespace Penumbra.CrashHandler; -public sealed class GameEventLogWriter : IDisposable +public sealed class GameEventLogWriter(int pid) : IDisposable { - public readonly ICharacterBaseBufferWriter CharacterBase = CharacterBaseBuffer.CreateWriter(); - public readonly IModdedFileBufferWriter FileLoaded = ModdedFileBuffer.CreateWriter(); - public readonly IAnimationInvocationBufferWriter AnimationFuncInvoked = AnimationInvocationBuffer.CreateWriter(); + public readonly ICharacterBaseBufferWriter CharacterBase = CharacterBaseBuffer.CreateWriter(pid); + public readonly IModdedFileBufferWriter FileLoaded = ModdedFileBuffer.CreateWriter(pid); + public readonly IAnimationInvocationBufferWriter AnimationFuncInvoked = AnimationInvocationBuffer.CreateWriter(pid); public void Dispose() { diff --git a/Penumbra.CrashHandler/Program.cs b/Penumbra.CrashHandler/Program.cs index 0ea76ac6..94e90bfc 100644 --- a/Penumbra.CrashHandler/Program.cs +++ b/Penumbra.CrashHandler/Program.cs @@ -12,7 +12,7 @@ public class CrashHandler try { - using var reader = new GameEventLogReader(); + using var reader = new GameEventLogReader(pid); var parent = Process.GetProcessById(pid); using var handle = parent.SafeHandle; parent.WaitForExit(); diff --git a/Penumbra/Services/CrashHandlerService.cs b/Penumbra/Services/CrashHandlerService.cs index a3a35b78..c713d623 100644 --- a/Penumbra/Services/CrashHandlerService.cs +++ b/Penumbra/Services/CrashHandlerService.cs @@ -24,6 +24,8 @@ public sealed class CrashHandlerService : IDisposable, IService private readonly Configuration _config; private readonly ValidityChecker _validityChecker; + private string _tempExecutableDirectory = string.Empty; + public CrashHandlerService(FilenameService files, CommunicatorService communicator, ActorManager actors, ResourceLoader resourceLoader, Configuration config, ValidityChecker validityChecker) { @@ -54,6 +56,7 @@ public sealed class CrashHandlerService : IDisposable, IService } Unsubscribe(); + CleanExecutables(); } private Process? _child; @@ -177,11 +180,12 @@ public sealed class CrashHandlerService : IDisposable, IService try { - using var reader = new GameEventLogReader(); + using var reader = new GameEventLogReader(Environment.ProcessId); JsonObject jObj; 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; @@ -198,14 +202,31 @@ public sealed class CrashHandlerService : IDisposable, IService } } - private string CopyExecutables() + private void CleanExecutables() { var parent = Path.GetDirectoryName(_files.CrashHandlerExe)!; - var folder = Path.Combine(parent, "temp"); - Directory.CreateDirectory(folder); + foreach (var dir in Directory.EnumerateDirectories(parent, "temp_*")) + { + 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.*")) - File.Copy(file, Path.Combine(folder, Path.GetFileName(file)), true); - return Path.Combine(folder, Path.GetFileName(_files.CrashHandlerExe)); + File.Copy(file, Path.Combine(_tempExecutableDirectory, Path.GetFileName(file)), true); + return Path.Combine(_tempExecutableDirectory, Path.GetFileName(_files.CrashHandlerExe)); } public void LogAnimation(nint character, ModCollection collection, AnimationInvocationType type) @@ -213,10 +234,17 @@ public sealed class CrashHandlerService : IDisposable, IService if (_eventWriter == null) return; - var name = GetActorName(character); - lock (_eventWriter) + try { - _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) return; - var name = GetActorName(address); - - lock (_eventWriter) + try { - _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) return; - var dashIdx = manipulatedPath.Value.InternalName[0] == (byte)'|' ? manipulatedPath.Value.InternalName.IndexOf((byte)'|', 1) : -1; - if (dashIdx >= 0 && !Utf8GamePath.IsRooted(manipulatedPath.Value.InternalName.Substring(dashIdx + 1))) - return; - - var name = GetActorName(resolveData.AssociatedGameObject); - lock (_eventWriter) + try { - _eventWriter!.FileLoaded.WriteLine(resolveData.AssociatedGameObject, name.Span, resolveData.ModCollection.Name, - manipulatedPath.Value.InternalName.Span, originalPath.Path.Span); + var dashIdx = manipulatedPath.Value.InternalName[0] == (byte)'|' ? manipulatedPath.Value.InternalName.IndexOf((byte)'|', 1) : -1; + 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 { CloseEventWriter(); - _eventWriter = new GameEventLogWriter(); + _eventWriter = new GameEventLogWriter(Environment.ProcessId); Penumbra.Log.Debug("Opened new Event Writer for crash handler."); } catch (Exception ex)