diff options
Diffstat (limited to 'Ryujinx.Horizon/LogManager')
-rw-r--r-- | Ryujinx.Horizon/LogManager/LmIpcServer.cs | 54 | ||||
-rw-r--r-- | Ryujinx.Horizon/LogManager/LmLog.cs | 19 | ||||
-rw-r--r-- | Ryujinx.Horizon/LogManager/LmLogger.cs | 139 | ||||
-rw-r--r-- | Ryujinx.Horizon/LogManager/LmMain.cs | 14 |
4 files changed, 226 insertions, 0 deletions
diff --git a/Ryujinx.Horizon/LogManager/LmIpcServer.cs b/Ryujinx.Horizon/LogManager/LmIpcServer.cs new file mode 100644 index 00000000..7b757fe9 --- /dev/null +++ b/Ryujinx.Horizon/LogManager/LmIpcServer.cs @@ -0,0 +1,54 @@ +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; +using Ryujinx.Horizon.Sm; + +namespace Ryujinx.Horizon.LogManager +{ + class LmIpcServer + { + private const int LogMaxSessionsCount = 42; + + private const int PointerBufferSize = 0x400; + private const int MaxDomains = 31; + private const int MaxDomainObjects = 61; + + private const int MaxPortsCount = 1; + + private static readonly ManagerOptions _logManagerOptions = new ManagerOptions( + PointerBufferSize, + MaxDomains, + MaxDomainObjects, + false); + + private static readonly ServiceName _logServiceName = ServiceName.Encode("lm"); + + private SmApi _sm; + private ServerManager _serverManager; + + private LmLog _logServiceObject; + + public void Initialize() + { + HeapAllocator allocator = new HeapAllocator(); + + _sm = new SmApi(); + _sm.Initialize().AbortOnFailure(); + + _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, LogMaxSessionsCount); + + _logServiceObject = new LmLog(); + + _serverManager.RegisterObjectForServer(_logServiceObject, _logServiceName, LogMaxSessionsCount); + } + + public void ServiceRequests() + { + _serverManager.ServiceRequests(); + } + + public void Shutdown() + { + _serverManager.Dispose(); + } + } +} diff --git a/Ryujinx.Horizon/LogManager/LmLog.cs b/Ryujinx.Horizon/LogManager/LmLog.cs new file mode 100644 index 00000000..772465c4 --- /dev/null +++ b/Ryujinx.Horizon/LogManager/LmLog.cs @@ -0,0 +1,19 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Lm; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.LogManager +{ + partial class LmLog : IServiceObject + { + public LogDestination LogDestination { get; set; } = LogDestination.TargetManager; + + [CmifCommand(0)] + public Result OpenLogger(out LmLogger logger, [ClientProcessId] ulong clientProcessId) + { + logger = new LmLogger(this, clientProcessId); + + return Result.Success; + } + } +} diff --git a/Ryujinx.Horizon/LogManager/LmLogger.cs b/Ryujinx.Horizon/LogManager/LmLogger.cs new file mode 100644 index 00000000..461776cd --- /dev/null +++ b/Ryujinx.Horizon/LogManager/LmLogger.cs @@ -0,0 +1,139 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Lm; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ryujinx.Horizon.LogManager +{ + partial class LmLogger : IServiceObject + { + private readonly LmLog _log; + private readonly ulong _clientProcessId; + + public LmLogger(LmLog log, ulong clientProcessId) + { + _log = log; + _clientProcessId = clientProcessId; + } + + [CmifCommand(0)] + public Result Log([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] Span<byte> message) + { + if (!SetProcessId(message, _clientProcessId)) + { + return Result.Success; + } + + Logger.Guest?.Print(LogClass.ServiceLm, LogImpl(message)); + + return Result.Success; + } + + [CmifCommand(1)] + public Result SetDestination(LogDestination destination) + { + _log.LogDestination = destination; + + return Result.Success; + } + + private static bool SetProcessId(Span<byte> message, ulong processId) + { + ref LogPacketHeader header = ref MemoryMarshal.Cast<byte, LogPacketHeader>(message)[0]; + + uint expectedMessageSize = (uint)Unsafe.SizeOf<LogPacketHeader>() + header.PayloadSize; + + if (expectedMessageSize != (uint)message.Length) + { + Logger.Warning?.Print(LogClass.ServiceLm, $"Invalid message size (expected 0x{expectedMessageSize:X} but got 0x{message.Length:X})."); + + return false; + } + + header.ProcessId = processId; + + return true; + } + + private static string LogImpl(ReadOnlySpan<byte> message) + { + SpanReader reader = new SpanReader(message); + + LogPacketHeader header = reader.Read<LogPacketHeader>(); + + StringBuilder sb = new StringBuilder(); + + sb.AppendLine($"Guest Log:\n Log level: {header.Severity}"); + + while (reader.Length > 0) + { + int type = ReadUleb128(ref reader); + int size = ReadUleb128(ref reader); + + LogDataChunkKey field = (LogDataChunkKey)type; + + string fieldStr = string.Empty; + + if (field == LogDataChunkKey.Start) + { + reader.Skip(size); + + continue; + } + else if (field == LogDataChunkKey.Stop) + { + break; + } + else if (field == LogDataChunkKey.Line) + { + fieldStr = $"{field}: {reader.Read<int>()}"; + } + else if (field == LogDataChunkKey.DropCount) + { + fieldStr = $"{field}: {reader.Read<long>()}"; + } + else if (field == LogDataChunkKey.Time) + { + fieldStr = $"{field}: {reader.Read<long>()}s"; + } + else if (field < LogDataChunkKey.Count) + { + fieldStr = $"{field}: '{Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd()}'"; + } + else + { + fieldStr = $"Field{field}: '{Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd()}'"; + } + + sb.AppendLine($" {fieldStr}"); + } + + return sb.ToString(); + } + + private static int ReadUleb128(ref SpanReader reader) + { + int result = 0; + int count = 0; + + byte encoded; + + do + { + encoded = reader.Read<byte>(); + + result += (encoded & 0x7F) << (7 * count); + + count++; + } while ((encoded & 0x80) != 0); + + return result; + } + } +} diff --git a/Ryujinx.Horizon/LogManager/LmMain.cs b/Ryujinx.Horizon/LogManager/LmMain.cs new file mode 100644 index 00000000..8c0262ac --- /dev/null +++ b/Ryujinx.Horizon/LogManager/LmMain.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Horizon.LogManager +{ + class LmMain : IService + { + public static void Main() + { + LmIpcServer ipcServer = new LmIpcServer(); + + ipcServer.Initialize(); + ipcServer.ServiceRequests(); + ipcServer.Shutdown(); + } + } +} |