aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Horizon/LogManager/Ipc
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Horizon/LogManager/Ipc')
-rw-r--r--Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs136
-rw-r--r--Ryujinx.Horizon/LogManager/Ipc/LogService.cs20
2 files changed, 156 insertions, 0 deletions
diff --git a/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs b/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs
new file mode 100644
index 00000000..002a5982
--- /dev/null
+++ b/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs
@@ -0,0 +1,136 @@
+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.Ipc
+{
+ partial class LmLogger : ILmLogger
+ {
+ private readonly LogService _log;
+ private readonly ulong _pid;
+
+ public LmLogger(LogService log, ulong pid)
+ {
+ _log = log;
+ _pid = pid;
+ }
+
+ [CmifCommand(0)]
+ public Result Log([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] Span<byte> message)
+ {
+ if (!SetProcessId(message, _pid))
+ {
+ return Result.Success;
+ }
+
+ Logger.Guest?.Print(LogClass.ServiceLm, LogImpl(message));
+
+ 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 static string LogImpl(ReadOnlySpan<byte> message)
+ {
+ SpanReader reader = new(message);
+ LogPacketHeader header = reader.Read<LogPacketHeader>();
+ StringBuilder builder = new();
+
+ builder.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;
+
+ 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()}'";
+ }
+
+ builder.AppendLine($" {fieldStr}");
+ }
+
+ return builder.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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Horizon/LogManager/Ipc/LogService.cs b/Ryujinx.Horizon/LogManager/Ipc/LogService.cs
new file mode 100644
index 00000000..6899739e
--- /dev/null
+++ b/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