diff --git a/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs b/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs index 002a59824..8b3acb101 100644 --- a/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs +++ b/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs @@ -1,6 +1,7 @@ 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; @@ -13,13 +14,19 @@ 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)] @@ -30,7 +37,12 @@ namespace Ryujinx.Horizon.LogManager.Ipc return Result.Success; } - Logger.Guest?.Print(LogClass.ServiceLm, LogImpl(message)); + if (LogImpl(message)) + { + Logger.Guest?.Print(LogClass.ServiceLm, _logPacket.ToString()); + + _logPacket = new LogPacket(); + } return Result.Success; } @@ -60,58 +72,86 @@ namespace Ryujinx.Horizon.LogManager.Ipc return true; } - private static string LogImpl(ReadOnlySpan message) + private bool LogImpl(ReadOnlySpan message) { - SpanReader reader = new(message); - LogPacketHeader header = reader.Read(); - StringBuilder builder = new(); + SpanReader reader = new(message); + LogPacketHeader header = reader.Read(); - builder.AppendLine($"Guest Log:\n Log level: {header.Severity}"); + 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 field = (LogDataChunkKey)type; + LogDataChunkKey key = (LogDataChunkKey)type; - string fieldStr; - - if (field == LogDataChunkKey.Start) + if (key == LogDataChunkKey.Start) { reader.Skip(size); continue; } - else if (field == LogDataChunkKey.Stop) + else if (key == LogDataChunkKey.Stop) { break; } - else if (field == LogDataChunkKey.Line) + else if (key == LogDataChunkKey.Line) { - fieldStr = $"{field}: {reader.Read()}"; + _logPacket.Line = reader.Read(); } - else if (field == LogDataChunkKey.DropCount) + else if (key == LogDataChunkKey.DropCount) { - fieldStr = $"{field}: {reader.Read()}"; + _logPacket.DropCount = reader.Read(); } - else if (field == LogDataChunkKey.Time) + else if (key == LogDataChunkKey.Time) { - fieldStr = $"{field}: {reader.Read()}s"; + _logPacket.Time = reader.Read(); } - else if (field < LogDataChunkKey.Count) + else if (key == LogDataChunkKey.Message) { - fieldStr = $"{field}: '{Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd()}'"; - } - else - { - fieldStr = $"Field{field}: '{Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd()}'"; - } + string text = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd(); - builder.AppendLine($" {fieldStr}"); + 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.GetSpan(size)).TrimEnd(); + } + else if (key == LogDataChunkKey.Function) + { + _logPacket.Function = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd(); + } + else if (key == LogDataChunkKey.Module) + { + _logPacket.Module = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd(); + } + else if (key == LogDataChunkKey.Thread) + { + _logPacket.Thread = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd(); + } + else if (key == LogDataChunkKey.ProgramName) + { + _logPacket.ProgramName = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd(); + } } - return builder.ToString(); + return isTailPacket; } private static int ReadUleb128(ref SpanReader reader) diff --git a/Ryujinx.Horizon/LogManager/Types/LogPacket.cs b/Ryujinx.Horizon/LogManager/Types/LogPacket.cs new file mode 100644 index 000000000..dbff5e3e2 --- /dev/null +++ b/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