forked from Mirror/Ryujinx
b5b7e23fc4
* hle: Simplify ServiceNotImplementedException This removes the need to pass in whether the command is a Tipc command or a Hipc command to the exception constructor. * hle: Use the IPC Message type to determine command type This allows differentiating between Tipc and Hipc commands when invoking a handler that supports handling both Tipc and Hipc commands.
172 lines
5.6 KiB
C#
172 lines
5.6 KiB
C#
using Ryujinx.Common;
|
|
using Ryujinx.HLE.HOS;
|
|
using Ryujinx.HLE.HOS.Ipc;
|
|
using Ryujinx.HLE.HOS.Services;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Runtime.Serialization;
|
|
using System.Text;
|
|
|
|
namespace Ryujinx.HLE.Exceptions
|
|
{
|
|
[Serializable]
|
|
internal class ServiceNotImplementedException : Exception
|
|
{
|
|
public IpcService Service { get; }
|
|
public ServiceCtx Context { get; }
|
|
public IpcMessage Request { get; }
|
|
|
|
public ServiceNotImplementedException(IpcService service, ServiceCtx context)
|
|
: this(service, context, "The service call is not implemented.")
|
|
{ }
|
|
|
|
public ServiceNotImplementedException(IpcService service, ServiceCtx context, string message)
|
|
: base(message)
|
|
{
|
|
Service = service;
|
|
Context = context;
|
|
Request = context.Request;
|
|
}
|
|
|
|
public ServiceNotImplementedException(IpcService service, ServiceCtx context, string message, Exception inner)
|
|
: base(message, inner)
|
|
{
|
|
Service = service;
|
|
Context = context;
|
|
Request = context.Request;
|
|
}
|
|
|
|
protected ServiceNotImplementedException(SerializationInfo info, StreamingContext context)
|
|
: base(info, context)
|
|
{ }
|
|
|
|
public override string Message
|
|
{
|
|
get
|
|
{
|
|
return base.Message +
|
|
Environment.NewLine +
|
|
Environment.NewLine +
|
|
BuildMessage();
|
|
}
|
|
}
|
|
|
|
private string BuildMessage()
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
// Print the IPC command details (service name, command ID, and handler)
|
|
(Type callingType, MethodBase callingMethod) = WalkStackTrace(new StackTrace(this));
|
|
|
|
if (callingType != null && callingMethod != null)
|
|
{
|
|
// If the type is past 0xF, we are using TIPC
|
|
var ipcCommands = Request.Type > IpcMessageType.TipcCloseSession ?
|
|
Service.TipcCommands : Service.HipcCommands;
|
|
|
|
// Find the handler for the method called
|
|
var ipcHandler = ipcCommands.FirstOrDefault(x => x.Value == callingMethod);
|
|
var ipcCommandId = ipcHandler.Key;
|
|
var ipcMethod = ipcHandler.Value;
|
|
|
|
if (ipcMethod != null)
|
|
{
|
|
sb.AppendLine($"Service Command: {Service.GetType().FullName}: {ipcCommandId} ({ipcMethod.Name})");
|
|
sb.AppendLine();
|
|
}
|
|
}
|
|
|
|
sb.AppendLine("Guest Stack Trace:");
|
|
sb.AppendLine(Context.Thread.GetGuestStackTrace());
|
|
|
|
// Print buffer information
|
|
if (Request.PtrBuff.Count > 0 ||
|
|
Request.SendBuff.Count > 0 ||
|
|
Request.ReceiveBuff.Count > 0 ||
|
|
Request.ExchangeBuff.Count > 0 ||
|
|
Request.RecvListBuff.Count > 0)
|
|
{
|
|
sb.AppendLine("Buffer Information:");
|
|
|
|
if (Request.PtrBuff.Count > 0)
|
|
{
|
|
sb.AppendLine("\tPtrBuff:");
|
|
|
|
foreach (var buff in Request.PtrBuff)
|
|
{
|
|
sb.AppendLine($"\t[{buff.Index}] Position: 0x{buff.Position:x16} Size: 0x{buff.Size:x16}");
|
|
}
|
|
}
|
|
|
|
if (Request.SendBuff.Count > 0)
|
|
{
|
|
sb.AppendLine("\tSendBuff:");
|
|
|
|
foreach (var buff in Request.SendBuff)
|
|
{
|
|
sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}");
|
|
}
|
|
}
|
|
|
|
if (Request.ReceiveBuff.Count > 0)
|
|
{
|
|
sb.AppendLine("\tReceiveBuff:");
|
|
|
|
foreach (var buff in Request.ReceiveBuff)
|
|
{
|
|
sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}");
|
|
}
|
|
}
|
|
|
|
if (Request.ExchangeBuff.Count > 0)
|
|
{
|
|
sb.AppendLine("\tExchangeBuff:");
|
|
|
|
foreach (var buff in Request.ExchangeBuff)
|
|
{
|
|
sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}");
|
|
}
|
|
}
|
|
|
|
if (Request.RecvListBuff.Count > 0)
|
|
{
|
|
sb.AppendLine("\tRecvListBuff:");
|
|
|
|
foreach (var buff in Request.RecvListBuff)
|
|
{
|
|
sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16}");
|
|
}
|
|
}
|
|
|
|
sb.AppendLine();
|
|
}
|
|
|
|
sb.AppendLine("Raw Request Data:");
|
|
sb.Append(HexUtils.HexTable(Request.RawData));
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private (Type, MethodBase) WalkStackTrace(StackTrace trace)
|
|
{
|
|
int i = 0;
|
|
|
|
StackFrame frame;
|
|
// Find the IIpcService method that threw this exception
|
|
while ((frame = trace.GetFrame(i++)) != null)
|
|
{
|
|
var method = frame.GetMethod();
|
|
var declType = method.DeclaringType;
|
|
|
|
if (typeof(IpcService).IsAssignableFrom(declType))
|
|
{
|
|
return (declType, method);
|
|
}
|
|
}
|
|
|
|
return (null, null);
|
|
}
|
|
}
|
|
}
|