Add an option to control which types of access events are logged

This commit is contained in:
Alex Barney 2019-09-30 16:15:31 -05:00
parent 22bbf07c2b
commit 5011a57d3e
9 changed files with 172 additions and 137 deletions

View file

@ -2,12 +2,12 @@
{ {
public static class U8StringHelpers public static class U8StringHelpers
{ {
public static U8String AsU8String(this string value) public static U8String ToU8String(this string value)
{ {
return new U8String(value); return new U8String(value);
} }
public static U8Span AsU8Span(this string value) public static U8Span ToU8Span(this string value)
{ {
return new U8Span(value); return new U8Span(value);
} }

View file

@ -0,0 +1,14 @@
using System;
namespace LibHac.Fs
{
public static class AccessLogHelpers
{
public static string BuildDefaultLogLine(Result result, TimeSpan startTime, TimeSpan endTime, int handleId,
string message, string caller)
{
return
$"FS_ACCESS: {{ start: {(long) startTime.TotalMilliseconds,9}, end: {(long) endTime.TotalMilliseconds,9}, result: 0x{result.Value:x8}, handle: 0x{handleId:x8}, function: \"{caller}\"{message} }}";
}
}
}

View file

@ -1,5 +1,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Accessors;
using LibHac.FsService; using LibHac.FsService;
namespace LibHac.Fs namespace LibHac.Fs
@ -14,16 +16,28 @@ namespace LibHac.Fs
public Result GetGlobalAccessLogMode(out GlobalAccessLogMode mode) public Result GetGlobalAccessLogMode(out GlobalAccessLogMode mode)
{ {
IFileSystemProxy fsProxy = GetFileSystemProxyServiceObject(); if (HasFileSystemServer())
{
IFileSystemProxy fsProxy = GetFileSystemProxyServiceObject();
return fsProxy.GetGlobalAccessLogMode(out mode); return fsProxy.GetGlobalAccessLogMode(out mode);
}
mode = GlobalAccessLogMode;
return Result.Success;
} }
public Result SetGlobalAccessLogMode(GlobalAccessLogMode mode) public Result SetGlobalAccessLogMode(GlobalAccessLogMode mode)
{ {
IFileSystemProxy fsProxy = GetFileSystemProxyServiceObject(); if (HasFileSystemServer())
{
IFileSystemProxy fsProxy = GetFileSystemProxyServiceObject();
return fsProxy.SetGlobalAccessLogMode(mode); return fsProxy.SetGlobalAccessLogMode(mode);
}
GlobalAccessLogMode = mode;
return Result.Success;
} }
public void SetLocalAccessLogMode(LocalAccessLogMode mode) public void SetLocalAccessLogMode(LocalAccessLogMode mode)
@ -31,6 +45,11 @@ namespace LibHac.Fs
LocalAccessLogMode = mode; LocalAccessLogMode = mode;
} }
public void SetAccessLogObject(IAccessLog accessLog)
{
AccessLog = accessLog;
}
internal bool IsEnabledAccessLog(LocalAccessLogMode mode) internal bool IsEnabledAccessLog(LocalAccessLogMode mode)
{ {
if ((LocalAccessLogMode & mode) == 0) if ((LocalAccessLogMode & mode) == 0)
@ -47,14 +66,21 @@ namespace LibHac.Fs
{ {
if (!AccessLogInitialized) if (!AccessLogInitialized)
{ {
IFileSystemProxy fsProxy = GetFileSystemProxyServiceObject(); if (HasFileSystemServer())
Result rc = fsProxy.GetGlobalAccessLogMode(out GlobalAccessLogMode globalMode);
GlobalAccessLogMode = globalMode;
if (rc.IsFailure())
{ {
throw new LibHacException("Abort"); IFileSystemProxy fsProxy = GetFileSystemProxyServiceObject();
Result rc = fsProxy.GetGlobalAccessLogMode(out GlobalAccessLogMode globalMode);
GlobalAccessLogMode = globalMode;
if (rc.IsFailure())
{
throw new LibHacException("Abort");
}
}
else
{
GlobalAccessLogMode = GlobalAccessLogMode.Log;
} }
if (GlobalAccessLogMode != GlobalAccessLogMode.None) if (GlobalAccessLogMode != GlobalAccessLogMode.None)
@ -74,7 +100,65 @@ namespace LibHac.Fs
} }
public Result RunOperationWithAccessLog(LocalAccessLogMode logType, Func<Result> operation, Func<string> textGenerator, [CallerMemberName] string caller = "") internal bool IsEnabledAccessLog()
{
return IsEnabledAccessLog(LocalAccessLogMode.All);
}
internal bool IsEnabledFileSystemAccessorAccessLog(string mountName)
{
if (MountTable.Find(mountName, out FileSystemAccessor accessor).IsFailure())
{
return true;
}
return accessor.IsAccessLogEnabled;
}
internal bool IsEnabledHandleAccessLog(FileHandle handle)
{
return handle.File.Parent.IsAccessLogEnabled;
}
internal bool IsEnabledHandleAccessLog(DirectoryHandle handle)
{
return handle.Directory.Parent.IsAccessLogEnabled;
}
internal void OutputAccessLog(Result result, TimeSpan startTime, TimeSpan endTime, string message, [CallerMemberName] string caller = "")
{
OutputAccessLogImpl(result, startTime, endTime, 0, message, caller);
}
internal void OutputAccessLog(Result result, TimeSpan startTime, TimeSpan endTime, FileHandle handle, string message, [CallerMemberName] string caller = "")
{
OutputAccessLogImpl(result, startTime, endTime, handle.GetId(), message, caller);
}
internal void OutputAccessLog(Result result, TimeSpan startTime, TimeSpan endTime, DirectoryHandle handle, string message, [CallerMemberName] string caller = "")
{
OutputAccessLogImpl(result, startTime, endTime, handle.GetId(), message, caller);
}
internal void OutputAccessLogImpl(Result result, TimeSpan startTime, TimeSpan endTime, int handleId,
string message, [CallerMemberName] string caller = "")
{
if (GlobalAccessLogMode.HasFlag(GlobalAccessLogMode.Log))
{
AccessLog?.Log(result, startTime, endTime, handleId, message, caller);
}
if (GlobalAccessLogMode.HasFlag(GlobalAccessLogMode.SdCard))
{
string logString = AccessLogHelpers.BuildDefaultLogLine(result, startTime, endTime, handleId, message, caller);
IFileSystemProxy fsProxy = GetFileSystemProxyServiceObject();
fsProxy.OutputAccessLogToSdCard(logString.ToU8Span());
}
}
public Result RunOperationWithAccessLog(LocalAccessLogMode logType, Func<Result> operation,
Func<string> textGenerator, [CallerMemberName] string caller = "")
{ {
Result rc; Result rc;
@ -93,6 +177,27 @@ namespace LibHac.Fs
return rc; return rc;
} }
public Result RunOperationWithAccessLog(LocalAccessLogMode logType, FileHandle handle, Func<Result> operation,
Func<string> textGenerator, [CallerMemberName] string caller = "")
{
Result rc;
if (IsEnabledAccessLog(logType) && handle.File.Parent.IsAccessLogEnabled)
{
TimeSpan startTime = Time.GetCurrent();
rc = operation();
TimeSpan endTime = Time.GetCurrent();
OutputAccessLog(rc, startTime, endTime, textGenerator(), caller);
}
else
{
rc = operation();
}
return rc;
}
} }
[Flags] [Flags]

View file

@ -73,22 +73,9 @@ namespace LibHac.Fs
public Result FlushFile(FileHandle handle) public Result FlushFile(FileHandle handle)
{ {
Result rc; return RunOperationWithAccessLog(LocalAccessLogMode.All, handle,
() => handle.File.Flush(),
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle)) () => string.Empty);
{
TimeSpan startTime = Time.GetCurrent();
rc = handle.File.Flush();
TimeSpan endTime = Time.GetCurrent();
OutputAccessLog(rc, startTime, endTime, handle, string.Empty);
}
else
{
rc = handle.File.Flush();
}
return rc;
} }
public Result GetFileSize(out long fileSize, FileHandle handle) public Result GetFileSize(out long fileSize, FileHandle handle)
@ -98,22 +85,9 @@ namespace LibHac.Fs
public Result SetFileSize(FileHandle handle, long size) public Result SetFileSize(FileHandle handle, long size)
{ {
Result rc; return RunOperationWithAccessLog(LocalAccessLogMode.All, handle,
() => handle.File.SetSize(size),
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle)) () => $", size: {size}");
{
TimeSpan startTime = Time.GetCurrent();
rc = handle.File.SetSize(size);
TimeSpan endTime = Time.GetCurrent();
OutputAccessLog(rc, startTime, endTime, handle, $", size: {size}");
}
else
{
rc = handle.File.SetSize(size);
}
return rc;
} }
public OpenMode GetFileOpenMode(FileHandle handle) public OpenMode GetFileOpenMode(FileHandle handle)
@ -123,18 +97,13 @@ namespace LibHac.Fs
public void CloseFile(FileHandle handle) public void CloseFile(FileHandle handle)
{ {
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle)) RunOperationWithAccessLog(LocalAccessLogMode.All, handle,
{ () =>
TimeSpan startTime = Time.GetCurrent(); {
handle.File.Dispose(); handle.File.Dispose();
TimeSpan endTime = Time.GetCurrent(); return Result.Success;
},
OutputAccessLog(Result.Success, startTime, endTime, handle, string.Empty); () => string.Empty);
}
else
{
handle.File.Dispose();
}
} }
} }
} }

View file

@ -1,5 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs.Accessors; using LibHac.Fs.Accessors;
using LibHac.FsService; using LibHac.FsService;
@ -16,19 +15,23 @@ namespace LibHac.Fs
internal ITimeSpanGenerator Time { get; } internal ITimeSpanGenerator Time { get; }
private IAccessLog AccessLog { get; set; } private IAccessLog AccessLog { get; set; }
private bool AccessLogEnabled { get; set; }
internal MountTable MountTable { get; } = new MountTable(); internal MountTable MountTable { get; } = new MountTable();
public FileSystemClient(ITimeSpanGenerator timer) public FileSystemClient(ITimeSpanGenerator timer)
{ {
Time = timer; Time = timer ?? new StopWatchTimeSpanGenerator();
} }
public FileSystemClient(FileSystemServer fsServer, ITimeSpanGenerator timer) public FileSystemClient(FileSystemServer fsServer, ITimeSpanGenerator timer)
{ {
FsSrv = fsServer; FsSrv = fsServer;
Time = timer; Time = timer ?? new StopWatchTimeSpanGenerator();
}
public bool HasFileSystemServer()
{
return FsSrv != null;
} }
public IFileSystemProxy GetFileSystemProxyServiceObject() public IFileSystemProxy GetFileSystemProxyServiceObject()
@ -39,7 +42,7 @@ namespace LibHac.Fs
{ {
if (FsProxy != null) return FsProxy; if (FsProxy != null) return FsProxy;
if (FsSrv == null) if (!HasFileSystemServer())
{ {
throw new InvalidOperationException("Client was not initialized with a server object."); throw new InvalidOperationException("Client was not initialized with a server object.");
} }
@ -87,18 +90,6 @@ namespace LibHac.Fs
rc.ThrowIfFailure(); rc.ThrowIfFailure();
} }
public void SetAccessLog(bool isEnabled, IAccessLog accessLog = null)
{
AccessLogEnabled = isEnabled;
if (isEnabled && FsSrv != null)
{
SetGlobalAccessLogMode(GlobalAccessLogMode.All);
}
if (accessLog != null) AccessLog = accessLog;
}
internal Result FindFileSystem(ReadOnlySpan<char> path, out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath) internal Result FindFileSystem(ReadOnlySpan<char> path, out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
{ {
fileSystem = default; fileSystem = default;
@ -147,45 +138,5 @@ namespace LibHac.Fs
return Result.Success; return Result.Success;
} }
internal bool IsEnabledAccessLog()
{
return AccessLogEnabled && AccessLog != null && Time != null;
}
internal bool IsEnabledFileSystemAccessorAccessLog(string mountName)
{
if (MountTable.Find(mountName, out FileSystemAccessor accessor).IsFailure())
{
return true;
}
return accessor.IsAccessLogEnabled;
}
internal bool IsEnabledHandleAccessLog(FileHandle handle)
{
return handle.File.Parent.IsAccessLogEnabled;
}
internal bool IsEnabledHandleAccessLog(DirectoryHandle handle)
{
return handle.Directory.Parent.IsAccessLogEnabled;
}
internal void OutputAccessLog(Result result, TimeSpan startTime, TimeSpan endTime, string message, [CallerMemberName] string caller = "")
{
AccessLog.Log(result, startTime, endTime, 0, message, caller);
}
internal void OutputAccessLog(Result result, TimeSpan startTime, TimeSpan endTime, FileHandle handle, string message, [CallerMemberName] string caller = "")
{
AccessLog.Log(result, startTime, endTime, handle.GetId(), message, caller);
}
internal void OutputAccessLog(Result result, TimeSpan startTime, TimeSpan endTime, DirectoryHandle handle, string message, [CallerMemberName] string caller = "")
{
AccessLog.Log(result, startTime, endTime, handle.GetId(), message, caller);
}
} }
} }

View file

@ -10,7 +10,7 @@ namespace hactoolnet
{ {
public void Log(Result result, TimeSpan startTime, TimeSpan endTime, int handleId, string message, [CallerMemberName] string caller = "") public void Log(Result result, TimeSpan startTime, TimeSpan endTime, int handleId, string message, [CallerMemberName] string caller = "")
{ {
Console.WriteLine(CommonAccessLog.BuildLogLine(result, startTime, endTime, handleId, message, caller)); Console.WriteLine(AccessLogHelpers.BuildDefaultLogLine(result, startTime, endTime, handleId, message, caller));
} }
} }
@ -24,7 +24,7 @@ namespace hactoolnet
public void Log(Result result, TimeSpan startTime, TimeSpan endTime, int handleId, string message, [CallerMemberName] string caller = "") public void Log(Result result, TimeSpan startTime, TimeSpan endTime, int handleId, string message, [CallerMemberName] string caller = "")
{ {
Logger.LogMessage(CommonAccessLog.BuildLogLine(result, startTime, endTime, handleId, message, caller)); Logger.LogMessage(AccessLogHelpers.BuildDefaultLogLine(result, startTime, endTime, handleId, message, caller));
} }
} }
@ -39,16 +39,7 @@ namespace hactoolnet
public void Log(Result result, TimeSpan startTime, TimeSpan endTime, int handleId, string message, [CallerMemberName] string caller = "") public void Log(Result result, TimeSpan startTime, TimeSpan endTime, int handleId, string message, [CallerMemberName] string caller = "")
{ {
Logger.WriteLine(CommonAccessLog.BuildLogLine(result, startTime, endTime, handleId, message, caller)); Logger.WriteLine(AccessLogHelpers.BuildDefaultLogLine(result, startTime, endTime, handleId, message, caller));
}
}
public static class CommonAccessLog
{
public static string BuildLogLine(Result result, TimeSpan startTime, TimeSpan endTime, int handleId, string message,
string caller)
{
return $"FS_ACCESS: {{ start: {(long)startTime.TotalMilliseconds,9}, end: {(long)endTime.TotalMilliseconds,9}, result: 0x{result.Value:x8}, handle: 0x{handleId:x8}, function: \"{caller}\"{message} }}";
} }
} }
} }

View file

@ -48,8 +48,8 @@ namespace hactoolnet
string mountName = $"section{i}"; string mountName = $"section{i}";
fs.Register(mountName.AsU8Span(), OpenFileSystem(i)); fs.Register(mountName.ToU8Span(), OpenFileSystem(i));
fs.Register("output".AsU8Span(), new LocalFileSystem(ctx.Options.SectionOutDir[i])); fs.Register("output".ToU8Span(), new LocalFileSystem(ctx.Options.SectionOutDir[i]));
FsUtils.CopyDirectoryWithProgress(fs, mountName + ":/", "output:/", logger: ctx.Logger); FsUtils.CopyDirectoryWithProgress(fs, mountName + ":/", "output:/", logger: ctx.Logger);
@ -97,8 +97,8 @@ namespace hactoolnet
{ {
FileSystemClient fs = ctx.Horizon.Fs; FileSystemClient fs = ctx.Horizon.Fs;
fs.Register("rom".AsU8Span(), OpenFileSystemByType(NcaSectionType.Data)); fs.Register("rom".ToU8Span(), OpenFileSystemByType(NcaSectionType.Data));
fs.Register("output".AsU8Span(), new LocalFileSystem(ctx.Options.RomfsOutDir)); fs.Register("output".ToU8Span(), new LocalFileSystem(ctx.Options.RomfsOutDir));
FsUtils.CopyDirectoryWithProgress(fs, "rom:/", "output:/", logger: ctx.Logger); FsUtils.CopyDirectoryWithProgress(fs, "rom:/", "output:/", logger: ctx.Logger);
@ -154,8 +154,8 @@ namespace hactoolnet
{ {
FileSystemClient fs = ctx.Horizon.Fs; FileSystemClient fs = ctx.Horizon.Fs;
fs.Register("code".AsU8Span(), OpenFileSystemByType(NcaSectionType.Code)); fs.Register("code".ToU8Span(), OpenFileSystemByType(NcaSectionType.Code));
fs.Register("output".AsU8Span(), new LocalFileSystem(ctx.Options.ExefsOutDir)); fs.Register("output".ToU8Span(), new LocalFileSystem(ctx.Options.ExefsOutDir));
FsUtils.CopyDirectoryWithProgress(fs, "code:/", "output:/", logger: ctx.Logger); FsUtils.CopyDirectoryWithProgress(fs, "code:/", "output:/", logger: ctx.Logger);

View file

@ -31,7 +31,7 @@ namespace hactoolnet
var save = new SaveDataFileSystem(ctx.Keyset, file, ctx.Options.IntegrityLevel, true); var save = new SaveDataFileSystem(ctx.Keyset, file, ctx.Options.IntegrityLevel, true);
FileSystemClient fs = ctx.Horizon.Fs; FileSystemClient fs = ctx.Horizon.Fs;
fs.Register("save".AsU8Span(), save); fs.Register("save".ToU8Span(), save);
if (ctx.Options.Validate) if (ctx.Options.Validate)
{ {
@ -40,7 +40,7 @@ namespace hactoolnet
if (ctx.Options.OutDir != null) if (ctx.Options.OutDir != null)
{ {
fs.Register("output".AsU8Span(), new LocalFileSystem(ctx.Options.OutDir)); fs.Register("output".ToU8Span(), new LocalFileSystem(ctx.Options.OutDir));
FsUtils.CopyDirectoryWithProgress(fs, "save:/", "output:/", logger: ctx.Logger); FsUtils.CopyDirectoryWithProgress(fs, "save:/", "output:/", logger: ctx.Logger);
@ -86,7 +86,7 @@ namespace hactoolnet
if (ctx.Options.RepackSource != null) if (ctx.Options.RepackSource != null)
{ {
fs.Register("input".AsU8Span(), new LocalFileSystem(ctx.Options.RepackSource)); fs.Register("input".ToU8Span(), new LocalFileSystem(ctx.Options.RepackSource));
fs.CleanDirectoryRecursively("save:/"); fs.CleanDirectoryRecursively("save:/");
fs.Commit("save"); fs.Commit("save");

View file

@ -2,6 +2,7 @@
using System.IO; using System.IO;
using System.Text; using System.Text;
using LibHac; using LibHac;
using LibHac.Fs;
namespace hactoolnet namespace hactoolnet
{ {
@ -63,7 +64,11 @@ namespace hactoolnet
{ {
logWriter = new StreamWriter(ctx.Options.AccessLog); logWriter = new StreamWriter(ctx.Options.AccessLog);
var accessLog = new TextWriterAccessLog(logWriter); var accessLog = new TextWriterAccessLog(logWriter);
ctx.Horizon.Fs.SetAccessLog(true, accessLog);
ctx.Horizon.Fs.SetLocalAccessLogMode(LocalAccessLogMode.All);
ctx.Horizon.Fs.SetGlobalAccessLogMode(GlobalAccessLogMode.Log);
ctx.Horizon.Fs.SetAccessLogObject(accessLog);
} }
OpenKeyset(ctx); OpenKeyset(ctx);