diff --git a/src/LibHac/Fs/AccessLog.cs b/src/LibHac/Fs/AccessLog.cs index 83a780c1..a6a474b3 100644 --- a/src/LibHac/Fs/AccessLog.cs +++ b/src/LibHac/Fs/AccessLog.cs @@ -1,13 +1,19 @@ using System; +using System.Buffers; using System.Buffers.Text; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Text; +using System.Text.Unicode; using LibHac.Common; using LibHac.Diag; +using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.Fs.Shim; using LibHac.FsSrv.Sf; using LibHac.Os; +using LibHac.Sf; +using static LibHac.Fs.Impl.AccessLogStrings; namespace LibHac.Fs { @@ -19,6 +25,8 @@ namespace LibHac.Fs public bool IsAccessLogInitialized; public SdkMutexType MutexForAccessLogInitialization; + public AccessLogImpl.AccessLogPrinterCallbackManager CallbackManager; + public void Initialize(FileSystemClient _) { MutexForAccessLogInitialization.Initialize(); @@ -49,14 +57,18 @@ namespace LibHac.Fs { using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - return fsProxy.Target.GetGlobalAccessLogMode(out mode); + Result rc = fsProxy.Target.GetGlobalAccessLogMode(out mode); + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result SetGlobalAccessLogMode(this FileSystemClient fs, GlobalAccessLogMode mode) { using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - return fsProxy.Target.SetGlobalAccessLogMode(mode); + Result rc = fsProxy.Target.SetGlobalAccessLogMode(mode); + fs.Impl.AbortIfNeeded(rc); + return rc; } public static void SetLocalAccessLog(this FileSystemClient fs, bool enabled) @@ -93,25 +105,10 @@ namespace LibHac.Fs } } - public static Result RunOperationWithAccessLog(this FileSystemClient fs, AccessLogTarget logTarget, - Func operation, Func textGenerator, [CallerMemberName] string caller = "") + public static void OutputApplicationInfoAccessLog(this FileSystemClient fs, in ApplicationInfo applicationInfo) { - Result rc; - - if (fs.Impl.IsEnabledAccessLog(logTarget)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = operation(); - Tick end = fs.Hos.Os.GetSystemTick(); - - fs.Impl.OutputAccessLog(rc, start, end, null, textGenerator().ToU8Span(), caller); - } - else - { - rc = operation(); - } - - return rc; + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + fsProxy.Target.OutputApplicationInfoAccessLog(in applicationInfo).IgnoreResult(); } } } @@ -274,78 +271,317 @@ namespace LibHac.Fs.Impl } } - internal static class AccessLogImpl + internal delegate int AccessLogPrinterCallback(Span textBuffer); + + public static class AccessLogImpl { internal static T DereferenceOutValue(in T value, Result result) where T : unmanaged { return result.IsSuccess() ? value : default; } - private static void GetProgramIndexForAccessLog(FileSystemClient fs, out int index, out int count) + private static U8Span GetPriorityRawName(FileSystemClientImpl fs, ref IdString idString) { - throw new NotImplementedException(); + // Note: The original function creates an IdString instead of taking one as a parameter. + // Because this might result in the function returning a string from its own stack frame, + // this implementation takes an IdString as a parameter. + return new U8Span(idString.ToString(fs.Fs.GetPriorityRawOnCurrentThreadForInternalUse())); } - private static void OutputAccessLogStart(FileSystemClient fs) + private static ref AccessLogPrinterCallbackManager GetStartAccessLogPrinterCallbackManager( + FileSystemClientImpl fs) { - throw new NotImplementedException(); + return ref fs.Globals.AccessLog.CallbackManager; } - private static void OutputAccessLogStartForSystem(FileSystemClient fs) + private static void FlushAccessLogOnSdCardImpl(FileSystemClientImpl fs) { - throw new NotImplementedException(); + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + fsProxy.Target.FlushAccessLogOnSdCard().IgnoreResult(); } - private static void OutputAccessLogStartGeneratedByCallback(FileSystemClient fs) + private static void OutputAccessLogToSdCardImpl(FileSystemClientImpl fs, U8Span message) { - throw new NotImplementedException(); + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + fsProxy.Target.OutputAccessLogToSdCard(new InBuffer(message.Value)).IgnoreResult(); } - public static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Tick start, Tick end, - FileHandle handle, U8Span message, [CallerMemberName] string functionName = "") + // The original function creates a string using the input format string and format args. + // We're not doing that in C#, so just pass the message through. + private static void OutputAccessLogToSdCard(this FileSystemClientImpl fs, U8Span message) { - throw new NotImplementedException(); + if (fs.Globals.AccessLog.GlobalAccessLogMode.HasFlag(GlobalAccessLogMode.SdCard)) + { + OutputAccessLogToSdCardImpl(fs, message); + } } - public static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Tick start, Tick end, - DirectoryHandle handle, U8Span message, [CallerMemberName] string functionName = "") + private static void OutputAccessLog(this FileSystemClientImpl fs, Result result, U8Span priority, Tick start, + Tick end, string functionName, object handle, U8Span message) { - throw new NotImplementedException(); + // ReSharper disable once RedundantAssignment + Span logBuffer = stackalloc byte[0]; + Span nameBuffer = stackalloc byte[0]; + + // 1.5x the UTF-16 char length should probably be enough. + int nameBufferSize = Math.Max(0x10, functionName.Length + (functionName.Length >> 1)); + + // Go straight to the fallback code if the function name is too long. + // Although to be honest, if this happens, saving on some allocations is probably the least of your worries. + if (nameBufferSize <= 0x400) + { + nameBuffer = stackalloc byte[nameBufferSize]; + OperationStatus status = Utf8.FromUtf16(functionName, nameBuffer, out _, out int bytesWritten); + + // Set the length to 0 if the buffer is too small to signify it should be handled by the fallback code. + // This will most likely never happen unless the function name has a lot of non-ASCII characters. + if (status == OperationStatus.DestinationTooSmall) + nameBuffer = Span.Empty; + else + nameBuffer = nameBuffer.Slice(0, bytesWritten); + } + + if (nameBuffer.Length == 0 && functionName.Length != 0) + { + nameBuffer = Encoding.UTF8.GetBytes(functionName); + } + + // Because the message is passed in as preformatted bytes instead of using sprintf like in the original, + // we can easily calculate the size of the buffer needed beforehand. + // The base text length is ~0x80-0x90 bytes long. Add another 0x70 as buffer space to get 0x100. + const int baseLength = 0x100; + int functionNameLength = nameBuffer.Length; + int logBufferSize = baseLength + functionNameLength + message.Length; + + // In case we need to rent an array. + RentedArray rentedLogBuffer = default; + + try + { + // Use the stack for buffers up to 1 KB since .NET stack sizes are usually massive anyway. + if (logBufferSize <= 0x400) + { + logBuffer = stackalloc byte[logBufferSize]; + } + else + { + rentedLogBuffer = new RentedArray(logBufferSize); + logBuffer = rentedLogBuffer.Array; + } + + OsState os = fs.Hos.Os; + long startMs = start.ToTimeSpan(os).GetMilliSeconds(); + long endMs = end.ToTimeSpan(os).GetMilliSeconds(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogLineStart) + .Append(LogStart).PadLeft((byte)' ', 9).AppendFormat(startMs) + .Append(LogEnd).PadLeft((byte)' ', 9).AppendFormat(endMs) + .Append(LogResult).AppendFormat(result.Value, 'x', 8) + .Append(LogHandle).AppendFormat((uint)(handle?.GetHashCode() ?? 0), 'x', + (byte)(Unsafe.SizeOf() * 2)) + .Append(LogPriority).Append(priority) + .Append(LogFunction).Append(nameBuffer).Append(LogQuote) + .Append(message) + .Append(LogLineEnd); + + OutputAccessLogImpl(fs, new U8Span(sb.Buffer)); + + } + finally + { + rentedLogBuffer.Dispose(); + } } - public static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Tick start, Tick end, - IdentifyAccessLogHandle handle, U8Span message, [CallerMemberName] string functionName = "") + private static void GetProgramIndexForAccessLog(FileSystemClientImpl fs, out int index, out int count) { - throw new NotImplementedException(); + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + Result rc = fsProxy.Target.GetProgramIndexForAccessLog(out index, out count); + Abort.DoAbortUnless(rc.IsSuccess()); } - public static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Tick start, Tick end, - object handle, U8Span message, [CallerMemberName] string functionName = "") + private static void OutputAccessLogStart(FileSystemClientImpl fs) { - throw new NotImplementedException(); + Span logBuffer = stackalloc byte[0x80]; + + GetProgramIndexForAccessLog(fs, out int currentProgramIndex, out int programCount); + + var sb = new U8StringBuilder(logBuffer, true); + + if (programCount > 1) + { + sb.Append(LogLineStart).Append(LogSdkVersion).Append(LogLibHacVersion).Append(LogSpec).Append(LogNx) + .Append(LogProgramIndex).AppendFormat(currentProgramIndex).Append(LogLineEnd); + } + else + { + sb.Append(LogLineStart).Append(LogSdkVersion).Append(LogLibHacVersion).Append(LogSpec).Append(LogNx) + .Append(LogLineEnd); + } + + OutputAccessLogImpl(fs, new U8Span(sb.Buffer.Slice(0, sb.Length))); } - public static void OutputAccessLogToOnlySdCard(this FileSystemClientImpl fs, U8Span message) + private static void OutputAccessLogStartForSystem(FileSystemClientImpl fs) { - throw new NotImplementedException(); + Span logBuffer = stackalloc byte[0x60]; + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogLineStart).Append(LogSdkVersion).Append(LogLibHacVersion).Append(LogSpec).Append(LogNx) + .Append(LogForSystem).Append(LogLineEnd); + + OutputAccessLogImpl(fs, new U8Span(sb.Buffer.Slice(0, sb.Length))); } - public static void OutputAccessLogUnlessResultSuccess(this FileSystemClientImpl fs, Result result, Tick start, - Tick end, FileHandle handle, U8Span message, [CallerMemberName] string functionName = "") + private static void OutputAccessLogStartGeneratedByCallback(FileSystemClientImpl fs) { - throw new NotImplementedException(); + ref AccessLogPrinterCallbackManager manager = ref GetStartAccessLogPrinterCallbackManager(fs); + + if (manager.IsRegisteredCallback()) + { + Span logBuffer = stackalloc byte[0x80]; + int length = manager.InvokeCallback(logBuffer); + + if (length <= logBuffer.Length) + { + OutputAccessLogImpl(fs, new U8Span(logBuffer.Slice(0, length))); + } + } } - public static void OutputAccessLogUnlessResultSuccess(this FileSystemClientImpl fs, Result result, Tick start, - Tick end, DirectoryHandle handle, U8Span message, [CallerMemberName] string functionName = "") + /// + /// Outputs the provided message to the access log. should be trimmed to the length + /// of the message text, and should not be null-terminated. + /// + /// The to use. + /// The message to output to the access log. + private static void OutputAccessLogImpl(FileSystemClientImpl fs, U8Span message) { - throw new NotImplementedException(); + if (fs.Globals.AccessLog.GlobalAccessLogMode.HasFlag(GlobalAccessLogMode.Log)) + { + fs.Hos.Diag.Impl.LogImpl(FsModuleName, LogSeverity.Info, message); + } + + if (fs.Globals.AccessLog.GlobalAccessLogMode.HasFlag(GlobalAccessLogMode.SdCard)) + { + OutputAccessLogToSdCardImpl(fs, message.Slice(0, message.Length - 1)); + } } - public static void OutputAccessLogUnlessResultSuccess(this FileSystemClientImpl fs, Result result, Tick start, + internal struct AccessLogPrinterCallbackManager + { + private AccessLogPrinterCallback _callback; + + public bool IsRegisteredCallback() + { + return _callback is not null; + } + + public void RegisterCallback(AccessLogPrinterCallback callback) + { + Assert.Null(_callback); + _callback = callback; + } + + public int InvokeCallback(Span textBuffer) + { + Assert.True(IsRegisteredCallback()); + return _callback(textBuffer); + } + } + + internal static void RegisterStartAccessLogPrinterCallback(FileSystemClientImpl fs, + AccessLogPrinterCallback callback) + { + ref AccessLogPrinterCallbackManager manager = ref GetStartAccessLogPrinterCallbackManager(fs); + manager.RegisterCallback(callback); + } + + internal static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Priority priority, Tick start, Tick end, object handle, U8Span message, [CallerMemberName] string functionName = "") { - throw new NotImplementedException(); + var idString = new IdString(); + OutputAccessLog(fs, result, new U8Span(idString.ToString(priority)), start, end, functionName, handle, + message); + } + + internal static void OutputAccessLog(this FileSystemClientImpl fs, Result result, PriorityRaw priorityRaw, + Tick start, Tick end, object handle, U8Span message, [CallerMemberName] string functionName = "") + { + var idString = new IdString(); + OutputAccessLog(fs, result, new U8Span(idString.ToString(priorityRaw)), start, end, functionName, handle, + message); + } + + internal static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Tick start, Tick end, + FileHandle handle, U8Span message, [CallerMemberName] string functionName = "") + { + var idString = new IdString(); + OutputAccessLog(fs, result, GetPriorityRawName(fs, ref idString), start, end, functionName, handle.File, + message); + } + + internal static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Tick start, Tick end, + DirectoryHandle handle, U8Span message, [CallerMemberName] string functionName = "") + { + var idString = new IdString(); + OutputAccessLog(fs, result, GetPriorityRawName(fs, ref idString), start, end, functionName, + handle.Directory, message); + } + + internal static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Tick start, Tick end, + IdentifyAccessLogHandle handle, U8Span message, [CallerMemberName] string functionName = "") + { + var idString = new IdString(); + OutputAccessLog(fs, result, GetPriorityRawName(fs, ref idString), start, end, functionName, handle.Handle, + message); + } + + internal static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Tick start, Tick end, + object handle, U8Span message, [CallerMemberName] string functionName = "") + { + var idString = new IdString(); + OutputAccessLog(fs, result, GetPriorityRawName(fs, ref idString), start, end, functionName, handle, + message); + } + + internal static void OutputAccessLogToOnlySdCard(this FileSystemClientImpl fs, U8Span message) + { + fs.OutputAccessLogToSdCard(message); + } + + internal static void OutputAccessLogUnlessResultSuccess(this FileSystemClientImpl fs, Result result, Tick start, + Tick end, FileHandle handle, U8Span message, [CallerMemberName] string functionName = "") + { + if (result.IsFailure()) + { + var idString = new IdString(); + OutputAccessLog(fs, result, GetPriorityRawName(fs, ref idString), start, end, functionName, handle, + message); + } + } + + internal static void OutputAccessLogUnlessResultSuccess(this FileSystemClientImpl fs, Result result, Tick start, + Tick end, DirectoryHandle handle, U8Span message, [CallerMemberName] string functionName = "") + { + if (result.IsFailure()) + { + var idString = new IdString(); + OutputAccessLog(fs, result, GetPriorityRawName(fs, ref idString), start, end, functionName, handle, + message); + } + } + + internal static void OutputAccessLogUnlessResultSuccess(this FileSystemClientImpl fs, Result result, Tick start, + Tick end, object handle, U8Span message, [CallerMemberName] string functionName = "") + { + if (result.IsFailure()) + { + var idString = new IdString(); + OutputAccessLog(fs, result, GetPriorityRawName(fs, ref idString), start, end, functionName, handle, + message); + } } internal static bool IsEnabledAccessLog(this FileSystemClientImpl fs, AccessLogTarget target) @@ -355,33 +591,34 @@ namespace LibHac.Fs.Impl if ((g.LocalAccessLogTarget & target) == 0) return false; + if (g.IsAccessLogInitialized) + return g.GlobalAccessLogMode != GlobalAccessLogMode.None; + + using ScopedLock lk = ScopedLock.Lock(ref g.MutexForAccessLogInitialization); + + // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (!g.IsAccessLogInitialized) { - using ScopedLock lk = ScopedLock.Lock(ref g.MutexForAccessLogInitialization); - - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (!g.IsAccessLogInitialized) + if (g.LocalAccessLogTarget.HasFlag(AccessLogTarget.System)) { - if (g.LocalAccessLogTarget.HasFlag(AccessLogTarget.System)) - { - g.GlobalAccessLogMode = GlobalAccessLogMode.Log; - OutputAccessLogStartForSystem(fs.Fs); - OutputAccessLogStartGeneratedByCallback(fs.Fs); - } - else - { - Result rc = fs.Fs.GetGlobalAccessLogMode(out g.GlobalAccessLogMode); - if (rc.IsFailure()) Abort.DoAbort(rc); - - if (g.GlobalAccessLogMode != GlobalAccessLogMode.None) - { - OutputAccessLogStart(fs.Fs); - OutputAccessLogStartGeneratedByCallback(fs.Fs); - } - } - - g.IsAccessLogInitialized = true; + g.GlobalAccessLogMode = GlobalAccessLogMode.Log; + OutputAccessLogStartForSystem(fs); + OutputAccessLogStartGeneratedByCallback(fs); } + else + { + Result rc = fs.Fs.GetGlobalAccessLogMode(out g.GlobalAccessLogMode); + fs.LogErrorMessage(rc); + if (rc.IsFailure()) Abort.DoAbort(rc); + + if (g.GlobalAccessLogMode != GlobalAccessLogMode.None) + { + OutputAccessLogStart(fs); + OutputAccessLogStartGeneratedByCallback(fs); + } + } + + g.IsAccessLogInitialized = true; } return g.GlobalAccessLogMode != GlobalAccessLogMode.None; @@ -392,7 +629,7 @@ namespace LibHac.Fs.Impl return fs.IsEnabledAccessLog(AccessLogTarget.All); } - public static bool IsEnabledHandleAccessLog(this FileSystemClientImpl _, FileHandle handle) + internal static bool IsEnabledHandleAccessLog(this FileSystemClientImpl _, FileHandle handle) { if (handle.File is null) return true; @@ -401,7 +638,7 @@ namespace LibHac.Fs.Impl return fsAccessor is not null && fsAccessor.IsEnabledAccessLog(); } - public static bool IsEnabledHandleAccessLog(this FileSystemClientImpl _, DirectoryHandle handle) + internal static bool IsEnabledHandleAccessLog(this FileSystemClientImpl _, DirectoryHandle handle) { if (handle.Directory is null) return true; @@ -409,12 +646,12 @@ namespace LibHac.Fs.Impl return handle.Directory.GetParent().IsEnabledAccessLog(); } - public static bool IsEnabledHandleAccessLog(this FileSystemClientImpl _, IdentifyAccessLogHandle handle) + internal static bool IsEnabledHandleAccessLog(this FileSystemClientImpl _, IdentifyAccessLogHandle handle) { return true; } - public static bool IsEnabledHandleAccessLog(this FileSystemClientImpl _, object handle) + internal static bool IsEnabledHandleAccessLog(this FileSystemClientImpl _, object handle) { if (handle is null) return true; @@ -425,34 +662,55 @@ namespace LibHac.Fs.Impl return false; } - public static bool IsEnabledFileSystemAccessorAccessLog(this FileSystemClientImpl fs, U8Span mountName) + internal static bool IsEnabledFileSystemAccessorAccessLog(this FileSystemClientImpl fs, U8Span mountName) { - throw new NotImplementedException(); + Result rc = fs.Find(out FileSystemAccessor accessor, mountName); + + if (rc.IsFailure()) + return true; + + return accessor.IsEnabledAccessLog(); } public static void EnableFileSystemAccessorAccessLog(this FileSystemClientImpl fs, U8Span mountName) { - throw new NotImplementedException(); + Result rc = fs.Find(out FileSystemAccessor fileSystem, mountName); + fs.LogErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); + + fileSystem.SetAccessLog(true); } - public static void FlushAccessLog(this FileSystemClientImpl fs) + internal static void FlushAccessLog(this FileSystemClientImpl fs) { - throw new NotImplementedException(); + Assert.True(false, $"Unsupported {nameof(FlushAccessLog)}"); } - public static void FlushAccessLogOnSdCard(this FileSystemClientImpl fs) + internal static void FlushAccessLogOnSdCard(this FileSystemClientImpl fs) { - throw new NotImplementedException(); + if (fs.Globals.AccessLog.GlobalAccessLogMode.HasFlag(GlobalAccessLogMode.SdCard)) + { + FlushAccessLogOnSdCardImpl(fs); + } } - public static ReadOnlySpan ConvertFromBoolToAccessLogBooleanValue(bool value) + internal static ReadOnlySpan ConvertFromBoolToAccessLogBooleanValue(bool value) { - return value ? AccessLogStrings.LogTrue : AccessLogStrings.LogFalse; + return value ? LogTrue : LogFalse; } } internal static class AccessLogStrings { + public static ReadOnlySpan FsModuleName => // "$fs" + new[] { (byte)'$', (byte)'f', (byte)'s' }; + + public static ReadOnlySpan LogLibHacVersion => // "0.13.0" + new[] + { + (byte)'0', (byte)'.', (byte)'1', (byte)'3', (byte)'.', (byte)'0' + }; + public static byte LogQuote => (byte)'"'; public static ReadOnlySpan LogTrue => // "true" @@ -489,6 +747,13 @@ namespace LibHac.Fs.Impl (byte)',', (byte)' ', (byte)'s', (byte)'i', (byte)'z', (byte)'e', (byte)':', (byte)' ' }; + public static ReadOnlySpan LogReadSize => // ", read_size: " + new[] + { + (byte)',', (byte)' ', (byte)'r', (byte)'e', (byte)'a', (byte)'d', (byte)'_', (byte)'s', + (byte)'i', (byte)'z', (byte)'e', (byte)':', (byte)' ' + }; + public static ReadOnlySpan LogWriteOptionFlush => // ", write_option: Flush" new[] { @@ -684,5 +949,87 @@ namespace LibHac.Fs.Impl (byte)'t', (byte)'a', (byte)'s', (byte)'p', (byte)'a', (byte)'c', (byte)'e', (byte)'i', (byte)'d', (byte)':', (byte)' ' }; + + public static ReadOnlySpan LogSdkVersion => // "sdk_version: " + new[] + { + (byte)'s', (byte)'d', (byte)'k', (byte)'_', (byte)'v', (byte)'e', (byte)'r', (byte)'s', + (byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogSpec => // ", spec: " + new[] + { + (byte)',', (byte)' ', (byte)'s', (byte)'p', (byte)'e', (byte)'c', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogNx => // "NX" + new[] { (byte)'N', (byte)'X' }; + + public static ReadOnlySpan LogProgramIndex => // ", program_index: " + new[] + { + (byte)',', (byte)' ', (byte)'p', (byte)'r', (byte)'o', (byte)'g', (byte)'r', (byte)'a', + (byte)'m', (byte)'_', (byte)'i', (byte)'n', (byte)'d', (byte)'e', (byte)'x', (byte)':', + (byte)' ' + }; + + public static ReadOnlySpan LogForSystem => // ", for_system: true" + new[] + { + (byte)',', (byte)' ', (byte)'f', (byte)'o', (byte)'r', (byte)'_', (byte)'s', (byte)'y', + (byte)'s', (byte)'t', (byte)'e', (byte)'m', (byte)':', (byte)' ', (byte)'t', (byte)'r', + (byte)'u', (byte)'e' + }; + + public static ReadOnlySpan LogLineStart => // "FS_ACCESS: { " + new[] + { + (byte)'F', (byte)'S', (byte)'_', (byte)'A', (byte)'C', (byte)'C', (byte)'E', (byte)'S', + (byte)'S', (byte)':', (byte)' ', (byte)'{', (byte)' ' + }; + + public static ReadOnlySpan LogLineEnd => // " }\n" + new[] { (byte)' ', (byte)'}', (byte)'\n' }; + + public static ReadOnlySpan LogStart => // "start: " + new[] + { + (byte)'s', (byte)'t', (byte)'a', (byte)'r', (byte)'t', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogEnd => // ", end: " + new[] + { + (byte)',', (byte)' ', (byte)'e', (byte)'n', (byte)'d', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogResult => // ", result: 0x" + new[] + { + (byte)',', (byte)' ', (byte)'r', (byte)'e', (byte)'s', (byte)'u', (byte)'l', (byte)'t', + (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + public static ReadOnlySpan LogHandle => // ", handle: 0x" + new[] + { + (byte)',', (byte)' ', (byte)'h', (byte)'a', (byte)'n', (byte)'d', (byte)'l', (byte)'e', + (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + public static ReadOnlySpan LogPriority => // ", priority: " + new[] + { + (byte)',', (byte)' ', (byte)'p', (byte)'r', (byte)'i', (byte)'o', (byte)'r', (byte)'i', + (byte)'t', (byte)'y', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogFunction => // ", function: "" + new[] + { + (byte)',', (byte)' ', (byte)'f', (byte)'u', (byte)'n', (byte)'c', (byte)'t', (byte)'i', + (byte)'o', (byte)'n', (byte)':', (byte)' ', (byte)'"' + }; } } diff --git a/src/LibHac/Fs/Fsa/FileAccessor.cs b/src/LibHac/Fs/Fsa/FileAccessor.cs index 236faf48..a8321b7f 100644 --- a/src/LibHac/Fs/Fsa/FileAccessor.cs +++ b/src/LibHac/Fs/Fsa/FileAccessor.cs @@ -3,6 +3,8 @@ using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Diag; using LibHac.Fs.Fsa; +using LibHac.Os; +using static LibHac.Fs.Impl.AccessLogStrings; // ReSharper disable once CheckNamespace namespace LibHac.Fs.Impl @@ -25,8 +27,13 @@ namespace LibHac.Fs.Impl // ReSharper disable once NotAccessedField.Local private int _pathHashIndex; - public FileAccessor(ref IFile file, FileSystemAccessor parentFileSystem, OpenMode mode) + private FileSystemClient _fsClient; + + public FileAccessor(FileSystemClient fsClient, ref IFile file, FileSystemAccessor parentFileSystem, + OpenMode mode) { + _fsClient = fsClient; + _file = Shared.Move(ref file); _parentFileSystem = parentFileSystem; _openMode = mode; @@ -79,9 +86,27 @@ namespace LibHac.Fs.Impl { Unsafe.SkipInit(out bytesRead); + Result rc; + Span logBuffer = stackalloc byte[0x50]; + var handle = new FileHandle(this); + if (_lastResult.IsFailure()) { - // Todo: Access log + if (_fsClient.Impl.IsEnabledAccessLog() && _fsClient.Impl.IsEnabledHandleAccessLog(handle)) + { + Tick start = _fsClient.Hos.Os.GetSystemTick(); + rc = _lastResult; + Tick end = _fsClient.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogOffset).AppendFormat(offset) + .Append(LogSize).AppendFormat(destination.Length) + .Append(LogReadSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in bytesRead, rc)); + + _fsClient.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(logBuffer), + nameof(UserFile.ReadFile)); + } + return _lastResult; } @@ -99,7 +124,26 @@ namespace LibHac.Fs.Impl } else { - return ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option); + if (_fsClient.Impl.IsEnabledAccessLog() && _fsClient.Impl.IsEnabledHandleAccessLog(handle)) + { + Tick start = _fsClient.Hos.Os.GetSystemTick(); + rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option); + Tick end = _fsClient.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogOffset).AppendFormat(offset) + .Append(LogSize).AppendFormat(destination.Length) + .Append(LogReadSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in bytesRead, rc)); + + _fsClient.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(logBuffer), + nameof(UserFile.ReadFile)); + } + else + { + rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option); + } + + return rc; } // ReSharper restore ConditionIsAlwaysTrueOrFalse } diff --git a/src/LibHac/Fs/Fsa/FileSystemAccessor.cs b/src/LibHac/Fs/Fsa/FileSystemAccessor.cs index 3d1d3e1c..95a79236 100644 --- a/src/LibHac/Fs/Fsa/FileSystemAccessor.cs +++ b/src/LibHac/Fs/Fsa/FileSystemAccessor.cs @@ -26,9 +26,14 @@ namespace LibHac.Fs.Impl private bool _isPathCacheAttached; private IMultiCommitTarget _multiCommitTarget; - public FileSystemAccessor(U8Span name, IMultiCommitTarget multiCommitTarget, IFileSystem fileSystem, - ICommonMountNameGenerator mountNameGenerator, ISaveDataAttributeGetter saveAttributeGetter) + private FileSystemClient _fsClient; + + public FileSystemAccessor(FileSystemClient fsClient, U8Span name, IMultiCommitTarget multiCommitTarget, + IFileSystem fileSystem, ICommonMountNameGenerator mountNameGenerator, + ISaveDataAttributeGetter saveAttributeGetter) { + _fsClient = fsClient; + _fileSystem = fileSystem; _openFiles = new LinkedList(); _openDirectories = new LinkedList(); @@ -270,7 +275,7 @@ namespace LibHac.Fs.Impl rc = _fileSystem.OpenFile(out iFile, path, mode); if (rc.IsFailure()) return rc; - var fileAccessor = new FileAccessor(ref iFile, this, mode); + var fileAccessor = new FileAccessor(_fsClient, ref iFile, this, mode); using (ScopedLock.Lock(ref _openListLock)) { diff --git a/src/LibHac/Fs/Fsa/Registrar.cs b/src/LibHac/Fs/Fsa/Registrar.cs index e8ee3440..f0f1e0f6 100644 --- a/src/LibHac/Fs/Fsa/Registrar.cs +++ b/src/LibHac/Fs/Fsa/Registrar.cs @@ -18,7 +18,7 @@ namespace LibHac.Fs.Fsa { public static Result Register(this FileSystemClient fs, U8Span name, IFileSystem fileSystem) { - var accessor = new FileSystemAccessor(name, null, fileSystem, null, null); + var accessor = new FileSystemAccessor(fs, name, null, fileSystem, null, null); fs.Impl.Register(accessor); return Result.Success; @@ -27,7 +27,7 @@ namespace LibHac.Fs.Fsa public static Result Register(this FileSystemClient fs, U8Span name, IFileSystem fileSystem, ICommonMountNameGenerator mountNameGenerator) { - var accessor = new FileSystemAccessor(name, null, fileSystem, mountNameGenerator, null); + var accessor = new FileSystemAccessor(fs, name, null, fileSystem, mountNameGenerator, null); fs.Impl.Register(accessor); return Result.Success; @@ -44,7 +44,7 @@ namespace LibHac.Fs.Fsa IFileSystem fileSystem, ICommonMountNameGenerator mountNameGenerator, ISaveDataAttributeGetter saveAttributeGetter, bool useDataCache, bool usePathCache) { - var accessor = new FileSystemAccessor(name, multiCommitTarget, fileSystem, mountNameGenerator, + var accessor = new FileSystemAccessor(fs, name, multiCommitTarget, fileSystem, mountNameGenerator, saveAttributeGetter); accessor.SetFileDataCacheAttachable(useDataCache); diff --git a/src/LibHac/Fs/Fsa/UserFileSystem.cs b/src/LibHac/Fs/Fsa/UserFileSystem.cs index 91f453fe..0895cccd 100644 --- a/src/LibHac/Fs/Fsa/UserFileSystem.cs +++ b/src/LibHac/Fs/Fsa/UserFileSystem.cs @@ -515,7 +515,7 @@ namespace LibHac.Fs.Fsa rc = fileSystem.OpenFile(out accessor, subPath, mode); Tick end = fs.Hos.Os.GetSystemTick(); - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer)); + fs.Impl.OutputAccessLog(rc, start, end, accessor, new U8Span(logBuffer)); } else { @@ -530,7 +530,7 @@ namespace LibHac.Fs.Fsa public static Result OpenFile(this FileSystemClient fs, out FileHandle handle, IFile file, OpenMode mode) { - var accessor = new FileAccessor(ref file, null, mode); + var accessor = new FileAccessor(fs, ref file, null, mode); handle = new FileHandle(accessor); return Result.Success; @@ -572,7 +572,7 @@ namespace LibHac.Fs.Fsa rc = fileSystem.OpenDirectory(out accessor, subPath, mode); Tick end = fs.Hos.Os.GetSystemTick(); - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer)); + fs.Impl.OutputAccessLog(rc, start, end, accessor, new U8Span(logBuffer)); } else { diff --git a/src/LibHac/Fs/Shim/Priority.cs b/src/LibHac/Fs/Shim/Priority.cs index dc94df77..52a820ed 100644 --- a/src/LibHac/Fs/Shim/Priority.cs +++ b/src/LibHac/Fs/Shim/Priority.cs @@ -36,6 +36,12 @@ namespace LibHac.Fs.Shim // Todo } + public static PriorityRaw GetPriorityRawOnCurrentThreadForInternalUse(this FileSystemClient fs) + { + // Todo + return PriorityRaw.Normal; + } + public static PriorityRaw GetPriorityRawOnCurrentThread(this FileSystemClient fs) { // Todo