diff --git a/src/LibHac/Fs/MemoryReportInfo.cs b/src/LibHac/Fs/MemoryReportInfo.cs index 92d43c07..aeff07d7 100644 --- a/src/LibHac/Fs/MemoryReportInfo.cs +++ b/src/LibHac/Fs/MemoryReportInfo.cs @@ -5,17 +5,16 @@ namespace LibHac.Fs [StructLayout(LayoutKind.Sequential, Size = 0x80)] public struct MemoryReportInfo { - long PooledBufferFreeSizePeak; - long PooledBufferRetriedCount; - long PooledBufferReduceAllocationCount; - long BufferManagerFreeSizePeak; - long BufferManagerRetiredCount; - long ExpHeapFreeSizePeak; - long BufferPoolFreeSizePeak; - long PatrolAllocateSuccessCount; - long PatrolAllocateFailureCount; - long BufferManagerTotalAllocatableSizePeak; - long BufferPoolAllocateSizeMax; - }; - + public long PooledBufferFreeSizePeak; + public long PooledBufferRetriedCount; + public long PooledBufferReduceAllocationCount; + public long BufferManagerFreeSizePeak; + public long BufferManagerRetriedCount; + public long ExpHeapFreeSizePeak; + public long BufferPoolFreeSizePeak; + public long PatrolAllocateSuccessCount; + public long PatrolAllocateFailureCount; + public long BufferManagerTotalAllocatableSizePeak; + public long BufferPoolAllocateSizeMax; + } } diff --git a/src/LibHac/FsSrv/Delegates.cs b/src/LibHac/FsSrv/Delegates.cs index f8a64109..05a9eb05 100644 --- a/src/LibHac/FsSrv/Delegates.cs +++ b/src/LibHac/FsSrv/Delegates.cs @@ -9,4 +9,6 @@ namespace LibHac.FsSrv public delegate Result SaveTransferCmacGenerator(Span mac, ReadOnlySpan data, SaveDataTransferCryptoConfiguration.KeyIndex index, int keyGeneration); + + public delegate Result PatrolAllocateCountGetter(out long successCount, out long failureCount); } diff --git a/src/LibHac/FsSrv/FileSystemProxyConfiguration.cs b/src/LibHac/FsSrv/FileSystemProxyConfiguration.cs index d7de11da..e4556233 100644 --- a/src/LibHac/FsSrv/FileSystemProxyConfiguration.cs +++ b/src/LibHac/FsSrv/FileSystemProxyConfiguration.cs @@ -10,6 +10,7 @@ namespace LibHac.FsSrv public NcaFileSystemServiceImpl NcaFileSystemService { get; set; } public SaveDataFileSystemServiceImpl SaveDataFileSystemService { get; set; } public TimeServiceImpl TimeService { get; set; } + public StatusReportServiceImpl StatusReportService { get; set; } public ProgramRegistryServiceImpl ProgramRegistryService { get; set; } public AccessLogServiceImpl AccessLogService { get; set; } } diff --git a/src/LibHac/FsSrv/FileSystemProxyImpl.cs b/src/LibHac/FsSrv/FileSystemProxyImpl.cs index a830c084..9e49936f 100644 --- a/src/LibHac/FsSrv/FileSystemProxyImpl.cs +++ b/src/LibHac/FsSrv/FileSystemProxyImpl.cs @@ -28,6 +28,65 @@ namespace LibHac.FsSrv CurrentProcess = ulong.MaxValue; } + private Result GetProgramInfo(out ProgramInfo programInfo) + { + return FsProxyCore.ProgramRegistry.GetProgramInfo(out programInfo, CurrentProcess); + } + + private Result GetNcaFileSystemService(out NcaFileSystemService ncaFsService) + { + if (NcaFsService is null) + { + ncaFsService = null; + return ResultFs.PreconditionViolation.Log(); + } + + ncaFsService = NcaFsService.Target; + return Result.Success; + } + + private Result GetSaveDataFileSystemService(out SaveDataFileSystemService saveFsService) + { + if (SaveFsService is null) + { + saveFsService = null; + return ResultFs.PreconditionViolation.Log(); + } + + saveFsService = SaveFsService.Target; + return Result.Success; + } + + private BaseStorageService GetBaseStorageService() + { + return new BaseStorageService(FsProxyCore.Config.BaseStorageService, CurrentProcess); + } + + private BaseFileSystemService GetBaseFileSystemService() + { + return new BaseFileSystemService(FsProxyCore.Config.BaseFileSystemService, CurrentProcess); + } + + private TimeService GetTimeService() + { + return new TimeService(FsProxyCore.Config.TimeService, CurrentProcess); + } + + private StatusReportService GetStatusReportService() + { + return new StatusReportService(FsProxyCore.Config.StatusReportService); + } + + private ProgramIndexRegistryService GetProgramIndexRegistryService() + { + return new ProgramIndexRegistryService(FsProxyCore.Config.ProgramRegistryService, CurrentProcess); + } + + private AccessLogService GetAccessLogService() + { + return new AccessLogService(FsProxyCore.Config.AccessLogService, CurrentProcess); + } + public Result OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, in FspPath path, ulong id, FileSystemProxyType fsType) { @@ -863,7 +922,7 @@ namespace LibHac.FsSrv public Result GetAndClearErrorInfo(out FileSystemProxyErrorInfo errorInfo) { - throw new NotImplementedException(); + return GetStatusReportService().GetAndClearFileSystemProxyErrorInfo(out errorInfo); } public Result RegisterProgramIndexMapInfo(InBuffer programIndexMapInfoBuffer, int programCount) @@ -984,14 +1043,14 @@ namespace LibHac.FsSrv return ncaFsService.OpenRegisteredUpdatePartition(out fileSystem); } - public Result GetAndClearMemoryReportInfo(out MemoryReportInfo report) + public Result GetAndClearMemoryReportInfo(out MemoryReportInfo reportInfo) { - throw new NotImplementedException(); + return GetStatusReportService().GetAndClearMemoryReportInfo(out reportInfo); } public Result GetFsStackUsage(out uint stackUsage, FsStackUsageThreadType threadType) { - throw new NotImplementedException(); + return GetStatusReportService().GetFsStackUsage(out stackUsage, threadType); } public Result OverrideSaveDataTransferTokenSignVerificationKey(InBuffer key) @@ -1058,59 +1117,5 @@ namespace LibHac.FsSrv { return GetBaseFileSystemService().OpenBisWiper(out bisWiper, transferMemoryHandle, transferMemorySize); } - - private Result GetProgramInfo(out ProgramInfo programInfo) - { - return FsProxyCore.ProgramRegistry.GetProgramInfo(out programInfo, CurrentProcess); - } - - private Result GetNcaFileSystemService(out NcaFileSystemService ncaFsService) - { - if (NcaFsService is null) - { - ncaFsService = null; - return ResultFs.PreconditionViolation.Log(); - } - - ncaFsService = NcaFsService.Target; - return Result.Success; - } - - private Result GetSaveDataFileSystemService(out SaveDataFileSystemService saveFsService) - { - if (SaveFsService is null) - { - saveFsService = null; - return ResultFs.PreconditionViolation.Log(); - } - - saveFsService = SaveFsService.Target; - return Result.Success; - } - - private BaseStorageService GetBaseStorageService() - { - return new BaseStorageService(FsProxyCore.Config.BaseStorageService, CurrentProcess); - } - - private BaseFileSystemService GetBaseFileSystemService() - { - return new BaseFileSystemService(FsProxyCore.Config.BaseFileSystemService, CurrentProcess); - } - - private TimeService GetTimeService() - { - return new TimeService(FsProxyCore.Config.TimeService, CurrentProcess); - } - - private ProgramIndexRegistryService GetProgramIndexRegistryService() - { - return new ProgramIndexRegistryService(FsProxyCore.Config.ProgramRegistryService, CurrentProcess); - } - - private AccessLogService GetAccessLogService() - { - return new AccessLogService(FsProxyCore.Config.AccessLogService, CurrentProcess); - } } } diff --git a/src/LibHac/FsSrv/FileSystemServer.cs b/src/LibHac/FsSrv/FileSystemServer.cs index 4be6e4c0..123bb875 100644 --- a/src/LibHac/FsSrv/FileSystemServer.cs +++ b/src/LibHac/FsSrv/FileSystemServer.cs @@ -1,10 +1,12 @@ using System; +using LibHac.Diag; using LibHac.Fs; using LibHac.Fs.Impl; using LibHac.Fs.Shim; using LibHac.FsSrv.Creators; using LibHac.FsSrv.Impl; using LibHac.FsSrv.Sf; +using LibHac.FsSrv.Storage; using LibHac.Sm; namespace LibHac.FsSrv @@ -17,6 +19,7 @@ namespace LibHac.FsSrv private const ulong SpeedEmulationProgramIdMaximum = 0x100000000001FFF; private FileSystemProxyCoreImpl FsProxyCore { get; } + public StorageService Storage { get; } /// The client instance to be used for internal operations like save indexer access. public HorizonClient Hos { get; } @@ -48,6 +51,8 @@ namespace LibHac.FsSrv Hos = horizonClient; + Storage = new StorageService(this); + IsDebugMode = false; Timer = config.TimeSpanGenerator ?? new StopWatchTimeSpanGenerator(); @@ -155,6 +160,19 @@ namespace LibHac.FsSrv var saveFsService = new SaveDataFileSystemServiceImpl(in saveFsServiceConfig); + var statusReportServiceConfig = new StatusReportServiceImpl.Configuration(); + statusReportServiceConfig.NcaFsServiceImpl = ncaFsService; + statusReportServiceConfig.SaveFsServiceImpl = saveFsService; + statusReportServiceConfig.BufferManagerMemoryReport = null; + statusReportServiceConfig.ExpHeapMemoryReport = null; + statusReportServiceConfig.BufferPoolMemoryReport = null; + statusReportServiceConfig.GetPatrolAllocateCounts = null; + statusReportServiceConfig.MainThreadStackUsageReporter = new DummyStackUsageReporter(); + statusReportServiceConfig.IpcWorkerThreadStackUsageReporter = new DummyStackUsageReporter(); + statusReportServiceConfig.PipeLineWorkerThreadStackUsageReporter = new DummyStackUsageReporter(); + + var statusReportService = new StatusReportServiceImpl(in statusReportServiceConfig); + var accessLogServiceConfig = new AccessLogServiceImpl.Configuration(); accessLogServiceConfig.MinimumProgramIdForSdCardLog = 0x0100000000003000; accessLogServiceConfig.HorizonClient = Hos; @@ -169,6 +187,7 @@ namespace LibHac.FsSrv NcaFileSystemService = ncaFsService, SaveDataFileSystemService = saveFsService, TimeService = timeService, + StatusReportService = statusReportService, ProgramRegistryService = programRegistryService, AccessLogService = accessLogService }; @@ -238,6 +257,11 @@ namespace LibHac.FsSrv return Result.Success; } } + + private class DummyStackUsageReporter : IStackUsageReporter + { + public uint GetStackUsage() => 0; + } } /// @@ -268,6 +292,31 @@ namespace LibHac.FsSrv public ITimeSpanGenerator TimeSpanGenerator { get; set; } } + public class StorageService + { + internal FileSystemServer Fs; + private IStorageDeviceManagerFactory _factory; + + internal StorageService(FileSystemServer parentServer) + { + Fs = parentServer; + } + + public void SetStorageDeviceManagerFactory(IStorageDeviceManagerFactory factory) + { + Assert.NotNull(factory); + Assert.Null(_factory); + + _factory = factory; + } + + public IStorageDeviceManagerFactory GetStorageDeviceManagerFactory() + { + Assert.NotNull(_factory); + return _factory; + } + } + // Functions in the nn::fssrv::detail namespace use this struct. // Possibly move this to the main class if the separation doesn't seem necessary. internal struct FileSystemServerImpl diff --git a/src/LibHac/FsSrv/IStackUsageReporter.cs b/src/LibHac/FsSrv/IStackUsageReporter.cs new file mode 100644 index 00000000..e13e6e38 --- /dev/null +++ b/src/LibHac/FsSrv/IStackUsageReporter.cs @@ -0,0 +1,7 @@ +namespace LibHac.FsSrv +{ + public interface IStackUsageReporter + { + uint GetStackUsage(); + } +} diff --git a/src/LibHac/FsSrv/MemoryReport.cs b/src/LibHac/FsSrv/MemoryReport.cs new file mode 100644 index 00000000..36002780 --- /dev/null +++ b/src/LibHac/FsSrv/MemoryReport.cs @@ -0,0 +1,11 @@ +namespace LibHac.FsSrv +{ + public abstract class MemoryReport + { + public abstract long GetFreeSizePeak(); + public abstract long GetTotalAllocatableSizePeak(); + public abstract long GetRetriedCount(); + public abstract long GetAllocateSizeMax(); + public abstract void Clear(); + } +} diff --git a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs index 295967a4..a8b0bbb5 100644 --- a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs @@ -612,9 +612,23 @@ namespace LibHac.FsSrv return programId; } - public void ResetTemporaryStorageIndexer() + public Result GetSaveDataIndexCount(out int count) { - _config.SaveIndexerManager.ResetIndexer(SaveDataSpaceId.Temporary); + Unsafe.SkipInit(out count); + + SaveDataIndexerAccessor accessor = null; + try + { + Result rc = OpenSaveDataIndexerAccessor(out accessor, out bool _, SaveDataSpaceId.User); + if (rc.IsFailure()) return rc; + + count = accessor.Indexer.GetIndexCount(); + return Result.Success; + } + finally + { + accessor?.Dispose(); + } } public Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, @@ -622,5 +636,10 @@ namespace LibHac.FsSrv { return _config.SaveIndexerManager.OpenSaveDataIndexerAccessor(out accessor, out neededInit, spaceId); } + + public void ResetTemporaryStorageIndexer() + { + _config.SaveIndexerManager.ResetIndexer(SaveDataSpaceId.Temporary); + } } } diff --git a/src/LibHac/FsSrv/Sf/IFileSystemProxy.cs b/src/LibHac/FsSrv/Sf/IFileSystemProxy.cs index dcc4f843..726687ae 100644 --- a/src/LibHac/FsSrv/Sf/IFileSystemProxy.cs +++ b/src/LibHac/FsSrv/Sf/IFileSystemProxy.cs @@ -114,7 +114,7 @@ namespace LibHac.FsSrv.Sf Result OutputAccessLogToSdCard(InBuffer textBuffer); Result RegisterUpdatePartition(); Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable fileSystem); - Result GetAndClearMemoryReportInfo(out MemoryReportInfo report); + Result GetAndClearMemoryReportInfo(out MemoryReportInfo reportInfo); Result GetProgramIndexForAccessLog(out int programIndex, out int programCount); Result GetFsStackUsage(out uint stackUsage, FsStackUsageThreadType threadType); Result UnsetSaveDataRootPath(); diff --git a/src/LibHac/FsSrv/StatusReportService.cs b/src/LibHac/FsSrv/StatusReportService.cs new file mode 100644 index 00000000..948503c8 --- /dev/null +++ b/src/LibHac/FsSrv/StatusReportService.cs @@ -0,0 +1,142 @@ +using LibHac.Diag; +using LibHac.Fs; +using LibHac.Os; + +namespace LibHac.FsSrv +{ + public readonly struct StatusReportService + { + private readonly StatusReportServiceImpl _serviceImpl; + + public StatusReportService(StatusReportServiceImpl serviceImpl) + { + _serviceImpl = serviceImpl; + } + + public Result GetAndClearFileSystemProxyErrorInfo(out FileSystemProxyErrorInfo errorInfo) + { + return _serviceImpl.GetAndClearFileSystemProxyErrorInfo(out errorInfo); + } + + public Result GetAndClearMemoryReportInfo(out MemoryReportInfo reportInfo) + { + return _serviceImpl.GetAndClearMemoryReportInfo(out reportInfo); + } + + public Result GetFsStackUsage(out uint stackUsage, FsStackUsageThreadType threadType) + { + stackUsage = _serviceImpl.ReportStackUsage(threadType); + return Result.Success; + } + } + + public class StatusReportServiceImpl + { + private Configuration _config; + private SdkMutexType _mutex; + + public StatusReportServiceImpl(in Configuration configuration) + { + _config = configuration; + _mutex.Initialize(); + } + + public struct Configuration + { + public NcaFileSystemServiceImpl NcaFsServiceImpl; + public SaveDataFileSystemServiceImpl SaveFsServiceImpl; + // Missing: FatFileSystemCreator (Not an IFatFileSystemCreator) + public MemoryReport BufferManagerMemoryReport; + public MemoryReport ExpHeapMemoryReport; + public MemoryReport BufferPoolMemoryReport; + public PatrolAllocateCountGetter GetPatrolAllocateCounts; + public IStackUsageReporter MainThreadStackUsageReporter; + public IStackUsageReporter IpcWorkerThreadStackUsageReporter; + public IStackUsageReporter PipeLineWorkerThreadStackUsageReporter; + + public FileSystemServer FsServer; + } + + public Result GetAndClearFileSystemProxyErrorInfo(out FileSystemProxyErrorInfo errorInfo) + { + errorInfo = new FileSystemProxyErrorInfo(); + + _config.NcaFsServiceImpl.GetAndClearRomFsErrorInfo(out errorInfo.RomFsRemountForDataCorruptionCount, + out errorInfo.RomFsUnrecoverableDataCorruptionByRemountCount, + out errorInfo.RomFsRecoveredByInvalidateCacheCount); + + // Missing: GetFatInfo + + Result rc = _config.SaveFsServiceImpl.GetSaveDataIndexCount(out int count); + if (rc.IsFailure()) return rc; + + errorInfo.SaveDataIndexCount = count; + return Result.Success; + } + + public Result GetAndClearMemoryReportInfo(out MemoryReportInfo reportInfo) + { + using ScopedLock lk = ScopedLock.Lock(ref _mutex); + + reportInfo = new MemoryReportInfo(); + + // Missing: Get and clear pooled buffer stats + reportInfo.PooledBufferFreeSizePeak = 0; + reportInfo.PooledBufferRetriedCount = 0; + reportInfo.PooledBufferReduceAllocationCount = 0; + + MemoryReport report = _config.BufferManagerMemoryReport; + if (report != null) + { + reportInfo.BufferManagerFreeSizePeak = report.GetFreeSizePeak(); + reportInfo.BufferManagerTotalAllocatableSizePeak = report.GetTotalAllocatableSizePeak(); + reportInfo.BufferManagerRetriedCount = report.GetRetriedCount(); + report.Clear(); + } + + report = _config.ExpHeapMemoryReport; + if (report != null) + { + reportInfo.ExpHeapFreeSizePeak = report.GetFreeSizePeak(); + report.Clear(); + } + + report = _config.BufferPoolMemoryReport; + if (report != null) + { + reportInfo.BufferPoolFreeSizePeak = report.GetFreeSizePeak(); + reportInfo.BufferPoolAllocateSizeMax = report.GetAllocateSizeMax(); + report.Clear(); + } + + if (_config.GetPatrolAllocateCounts != null) + { + _config.GetPatrolAllocateCounts(out reportInfo.PatrolAllocateSuccessCount, + out reportInfo.PatrolAllocateFailureCount); + } + + return Result.Success; + } + + public uint ReportStackUsage(FsStackUsageThreadType threadType) + { + switch (threadType) + { + case FsStackUsageThreadType.MainThread: + Assert.NotNull(_config.MainThreadStackUsageReporter); + return _config.MainThreadStackUsageReporter.GetStackUsage(); + + case FsStackUsageThreadType.IpcWorker: + Assert.NotNull(_config.IpcWorkerThreadStackUsageReporter); + return _config.IpcWorkerThreadStackUsageReporter.GetStackUsage(); + + case FsStackUsageThreadType.PipelineWorker: + Assert.NotNull(_config.PipeLineWorkerThreadStackUsageReporter); + return _config.PipeLineWorkerThreadStackUsageReporter.GetStackUsage(); + + default: + return 0; + } + } + } +}