aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Horizon/LogManager
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Horizon/LogManager
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Horizon/LogManager')
-rw-r--r--src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs176
-rw-r--r--src/Ryujinx.Horizon/LogManager/Ipc/LogService.cs20
-rw-r--r--src/Ryujinx.Horizon/LogManager/LmIpcServer.cs43
-rw-r--r--src/Ryujinx.Horizon/LogManager/LmMain.cs17
-rw-r--r--src/Ryujinx.Horizon/LogManager/Types/LogPacket.cs72
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