diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Horizon/LogManager | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Horizon/LogManager')
| -rw-r--r-- | src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs | 176 | ||||
| -rw-r--r-- | src/Ryujinx.Horizon/LogManager/Ipc/LogService.cs | 20 | ||||
| -rw-r--r-- | src/Ryujinx.Horizon/LogManager/LmIpcServer.cs | 43 | ||||
| -rw-r--r-- | src/Ryujinx.Horizon/LogManager/LmMain.cs | 17 | ||||
| -rw-r--r-- | src/Ryujinx.Horizon/LogManager/Types/LogPacket.cs | 72 |
5 files changed, 328 insertions, 0 deletions
diff --git a/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs b/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs new file mode 100644 index 00000000..e930bdd7 --- /dev/null +++ b/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs @@ -0,0 +1,176 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.LogManager.Types; +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.Ipc +{ + partial class LmLogger : ILmLogger + { + private const int MessageLengthLimit = 5000; + + private readonly LogService _log; + private readonly ulong _pid; + + private LogPacket _logPacket; + + public LmLogger(LogService log, ulong pid) + { + _log = log; + _pid = pid; + + _logPacket = new LogPacket(); + } + + [CmifCommand(0)] + public Result Log([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] Span<byte> message) + { + if (!SetProcessId(message, _pid)) + { + return Result.Success; + } + + if (LogImpl(message)) + { + Logger.Guest?.Print(LogClass.ServiceLm, _logPacket.ToString()); + + _logPacket = new LogPacket(); + } + + return Result.Success; + } + + [CmifCommand(1)] // 3.0.0+ + 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 bool LogImpl(ReadOnlySpan<byte> message) + { + SpanReader reader = new(message); + LogPacketHeader header = reader.Read<LogPacketHeader>(); + + bool isHeadPacket = (header.Flags & LogPacketFlags.IsHead) != 0; + bool isTailPacket = (header.Flags & LogPacketFlags.IsTail) != 0; + + _logPacket.Severity = header.Severity; + + while (reader.Length > 0) + { + int type = ReadUleb128(ref reader); + int size = ReadUleb128(ref reader); + + LogDataChunkKey key = (LogDataChunkKey)type; + + if (key == LogDataChunkKey.Start) + { + reader.Skip(size); + + continue; + } + else if (key == LogDataChunkKey.Stop) + { + break; + } + else if (key == LogDataChunkKey.Line) + { + _logPacket.Line = reader.Read<int>(); + } + else if (key == LogDataChunkKey.DropCount) + { + _logPacket.DropCount = reader.Read<long>(); + } + else if (key == LogDataChunkKey.Time) + { + _logPacket.Time = reader.Read<long>(); + } + else if (key == LogDataChunkKey.Message) + { + string text = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + + if (isHeadPacket && isTailPacket) + { + _logPacket.Message = text; + } + else + { + _logPacket.Message += text; + + if (_logPacket.Message.Length >= MessageLengthLimit) + { + isTailPacket = true; + } + } + } + else if (key == LogDataChunkKey.Filename) + { + _logPacket.Filename = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + } + else if (key == LogDataChunkKey.Function) + { + _logPacket.Function = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + } + else if (key == LogDataChunkKey.Module) + { + _logPacket.Module = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + } + else if (key == LogDataChunkKey.Thread) + { + _logPacket.Thread = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + } + else if (key == LogDataChunkKey.ProgramName) + { + _logPacket.ProgramName = Encoding.UTF8.GetString(reader.GetSpanSafe(size)).TrimEnd(); + } + } + + return isTailPacket; + } + + 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; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Horizon/LogManager/Ipc/LogService.cs b/src/Ryujinx.Horizon/LogManager/Ipc/LogService.cs new file mode 100644 index 00000000..6899739e --- /dev/null +++ b/src/Ryujinx.Horizon/LogManager/Ipc/LogService.cs @@ -0,0 +1,20 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Lm; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.LogManager.Ipc +{ + partial class LogService : ILogService + { + public LogDestination LogDestination { get; set; } = LogDestination.TargetManager; + + [CmifCommand(0)] + public Result OpenLogger(out LmLogger logger, [ClientProcessId] ulong pid) + { + // NOTE: Internal name is Logger, but we rename it LmLogger to avoid name clash with Ryujinx.Common.Logging logger. + logger = new LmLogger(this, pid); + + return Result.Success; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs b/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs new file mode 100644 index 00000000..71b844a2 --- /dev/null +++ b/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs @@ -0,0 +1,43 @@ +using Ryujinx.Horizon.LogManager.Ipc; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.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(PointerBufferSize, MaxDomains, MaxDomainObjects, false); + + private SmApi _sm; + private ServerManager _serverManager; + + public void Initialize() + { + HeapAllocator allocator = new(); + + _sm = new SmApi(); + _sm.Initialize().AbortOnFailure(); + + _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, LogMaxSessionsCount); + + _serverManager.RegisterObjectForServer(new LogService(), ServiceName.Encode("lm"), LogMaxSessionsCount); + } + + public void ServiceRequests() + { + _serverManager.ServiceRequests(); + } + + public void Shutdown() + { + _serverManager.Dispose(); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Horizon/LogManager/LmMain.cs b/src/Ryujinx.Horizon/LogManager/LmMain.cs new file mode 100644 index 00000000..bbe96d4c --- /dev/null +++ b/src/Ryujinx.Horizon/LogManager/LmMain.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Horizon.LogManager +{ + class LmMain : IService + { + public static void Main(ServiceTable serviceTable) + { + LmIpcServer ipcServer = new(); + + ipcServer.Initialize(); + + serviceTable.SignalServiceReady(); + + ipcServer.ServiceRequests(); + ipcServer.Shutdown(); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Horizon/LogManager/Types/LogPacket.cs b/src/Ryujinx.Horizon/LogManager/Types/LogPacket.cs new file mode 100644 index 00000000..dbff5e3e --- /dev/null +++ b/src/Ryujinx.Horizon/LogManager/Types/LogPacket.cs @@ -0,0 +1,72 @@ +using Ryujinx.Horizon.Sdk.Diag; +using System.Text; + +namespace Ryujinx.Horizon.LogManager.Types +{ + struct LogPacket + { + public string Message; + public int Line; + public string Filename; + public string Function; + public string Module; + public string Thread; + public long DropCount; + public long Time; + public string ProgramName; + public LogSeverity Severity; + + public override string ToString() + { + StringBuilder builder = new(); + builder.AppendLine($"Guest Log:\n Log level: {Severity}"); + + if (Time > 0) + { + builder.AppendLine($" Time: {Time}s"); + } + + if (DropCount > 0) + { + builder.AppendLine($" DropCount: {DropCount}"); + } + + if (!string.IsNullOrEmpty(ProgramName)) + { + builder.AppendLine($" ProgramName: {ProgramName}"); + } + + if (!string.IsNullOrEmpty(Module)) + { + builder.AppendLine($" Module: {Module}"); + } + + if (!string.IsNullOrEmpty(Thread)) + { + builder.AppendLine($" Thread: {Thread}"); + } + + if (!string.IsNullOrEmpty(Filename)) + { + builder.AppendLine($" Filename: {Filename}"); + } + + if (Line > 0) + { + builder.AppendLine($" Line: {Line}"); + } + + if (!string.IsNullOrEmpty(Function)) + { + builder.AppendLine($" Function: {Function}"); + } + + if (!string.IsNullOrEmpty(Message)) + { + builder.AppendLine($" Message: {Message}"); + } + + return builder.ToString(); + } + } +}
\ No newline at end of file |
