Stub a few services, add support for generating call stacks on the CPU

This commit is contained in:
gdkchan 2018-04-22 01:21:49 -03:00
parent 4906acdde9
commit bd9b1e2c6b
20 changed files with 267 additions and 41 deletions

View file

@ -1,4 +1,6 @@
public static class AOptimizations
{
public static bool DisableMemoryChecks = false;
public static bool GenerateCallStack = true;
}

View file

@ -8,8 +8,6 @@ namespace ChocolArm64.Instruction
{
static partial class AInstEmit
{
private const BindingFlags Binding = BindingFlags.NonPublic | BindingFlags.Instance;
public static void Brk(AILEmitterCtx Context)
{
EmitExceptionCall(Context, nameof(AThreadState.OnBreak));
@ -30,9 +28,7 @@ namespace ChocolArm64.Instruction
Context.EmitLdc_I4(Op.Id);
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
Context.EmitCall(MthdInfo);
Context.EmitPrivateCall(typeof(AThreadState), MthdName);
//Check if the thread should still be running, if it isn't then we return 0
//to force a return to the dispatcher and then exit the thread.
@ -73,11 +69,7 @@ namespace ChocolArm64.Instruction
Context.EmitLdc_I8(Op.Position);
Context.EmitLdc_I4(Op.RawOpCode);
string MthdName = nameof(AThreadState.OnUndefined);
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
Context.EmitCall(MthdInfo);
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.OnUndefined));
if (Context.CurrBlock.Next != null)
{

View file

@ -35,6 +35,14 @@ namespace ChocolArm64.Instruction
{
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
if (AOptimizations.GenerateCallStack)
{
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitLdc_I8(Op.Imm);
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod));
}
Context.EmitLdc_I(Op.Position + 4);
Context.EmitStint(AThreadState.LRIndex);
Context.EmitStoreState();
@ -72,6 +80,14 @@ namespace ChocolArm64.Instruction
{
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
if (AOptimizations.GenerateCallStack)
{
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitLdintzr(Op.Rn);
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod));
}
Context.EmitLdc_I(Op.Position + 4);
Context.EmitStint(AThreadState.LRIndex);
Context.EmitStoreState();
@ -84,6 +100,14 @@ namespace ChocolArm64.Instruction
{
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
if (AOptimizations.GenerateCallStack)
{
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitLdintzr(Op.Rn);
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.JumpMethod));
}
Context.EmitStoreState();
Context.EmitLdintzr(Op.Rn);
@ -105,6 +129,13 @@ namespace ChocolArm64.Instruction
public static void Ret(AILEmitterCtx Context)
{
if (AOptimizations.GenerateCallStack)
{
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.ExitMethod));
}
Context.EmitStoreState();
Context.EmitLdint(AThreadState.LRIndex);

View file

@ -1,5 +1,6 @@
using ChocolArm64.Events;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ChocolArm64.State
@ -56,10 +57,17 @@ namespace ChocolArm64.State
public event EventHandler<AInstExceptionEventArgs> SvcCall;
public event EventHandler<AInstUndefinedEventArgs> Undefined;
private Stack<long> CallStack;
private static Stopwatch TickCounter;
private static double HostTickFreq;
public AThreadState()
{
CallStack = new Stack<long>();
}
static AThreadState()
{
HostTickFreq = 1.0 / Stopwatch.Frequency;
@ -83,5 +91,27 @@ namespace ChocolArm64.State
{
Undefined?.Invoke(this, new AInstUndefinedEventArgs(Position, RawOpCode));
}
internal void EnterMethod(long Position)
{
CallStack.Push(Position);
}
internal void ExitMethod()
{
CallStack.TryPop(out _);
}
internal void JumpMethod(long Position)
{
CallStack.TryPop(out _);
CallStack.Push(Position);
}
public long[] GetCallStack()
{
return CallStack.ToArray();
}
}
}

View file

@ -461,6 +461,21 @@ namespace ChocolArm64.Translation
EmitCall(ObjType.GetMethod(MthdName));
}
public void EmitPrivateCall(Type ObjType, string MthdName)
{
if (ObjType == null)
{
throw new ArgumentNullException(nameof(ObjType));
}
if (MthdName == null)
{
throw new ArgumentNullException(nameof(MthdName));
}
EmitCall(ObjType.GetMethod(MthdName, BindingFlags.Instance | BindingFlags.NonPublic));
}
public void EmitCall(MethodInfo MthdInfo)
{
if (MthdInfo == null)

View file

@ -7,14 +7,16 @@ namespace Ryujinx.Core.Loaders
{
class Executable
{
private AMemory Memory;
private List<ElfDyn> Dynamic;
private Dictionary<long, string> m_SymbolTable;
public IReadOnlyDictionary<long, string> SymbolTable => m_SymbolTable;
public string Name { get; private set; }
private AMemory Memory;
public long ImageBase { get; private set; }
public long ImageEnd { get; private set; }
@ -24,6 +26,8 @@ namespace Ryujinx.Core.Loaders
m_SymbolTable = new Dictionary<long, string>();
Name = Exe.Name;
this.Memory = Memory;
this.ImageBase = ImageBase;
this.ImageEnd = ImageBase;

View file

@ -2,6 +2,8 @@ namespace Ryujinx.Core.Loaders.Executables
{
public interface IExecutable
{
string Name { get; }
byte[] Text { get; }
byte[] RO { get; }
byte[] Data { get; }

View file

@ -4,6 +4,8 @@ namespace Ryujinx.Core.Loaders.Executables
{
class Nro : IExecutable
{
public string Name { get; private set; }
public byte[] Text { get; private set; }
public byte[] RO { get; private set; }
public byte[] Data { get; private set; }
@ -14,8 +16,10 @@ namespace Ryujinx.Core.Loaders.Executables
public int DataOffset { get; private set; }
public int BssSize { get; private set; }
public Nro(Stream Input)
public Nro(Stream Input, string Name)
{
this.Name = Name;
BinaryReader Reader = new BinaryReader(Input);
Input.Seek(4, SeekOrigin.Begin);

View file

@ -6,6 +6,8 @@ namespace Ryujinx.Core.Loaders.Executables
{
class Nso : IExecutable
{
public string Name { get; private set; }
public byte[] Text { get; private set; }
public byte[] RO { get; private set; }
public byte[] Data { get; private set; }
@ -27,8 +29,10 @@ namespace Ryujinx.Core.Loaders.Executables
HasDataHash = 1 << 5
}
public Nso(Stream Input)
public Nso(Stream Input, string Name)
{
this.Name = Name;
BinaryReader Reader = new BinaryReader(Input);
Input.Seek(0, SeekOrigin.Begin);

View file

@ -16,6 +16,7 @@
ServiceApm,
ServiceAudio,
ServiceBsd,
ServiceCaps,
ServiceFriend,
ServiceFs,
ServiceHid,

View file

@ -58,7 +58,9 @@ namespace Ryujinx.Core.OsHle
using (FileStream Input = new FileStream(File, FileMode.Open))
{
Nso Program = new Nso(Input);
string Name = Path.GetFileNameWithoutExtension(File);
Nso Program = new Nso(Input, Name);
MainProcess.LoadProgram(Program);
}
@ -80,13 +82,15 @@ namespace Ryujinx.Core.OsHle
{
bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
string Name = Path.GetFileNameWithoutExtension(FileName);
Process MainProcess = MakeProcess();
using (FileStream Input = new FileStream(FileName, FileMode.Open))
{
MainProcess.LoadProgram(IsNro
? (IExecutable)new Nro(Input)
: (IExecutable)new Nso(Input));
? (IExecutable)new Nro(Input, Name)
: (IExecutable)new Nso(Input, Name));
}
MainProcess.SetEmptyArgs();

View file

@ -156,13 +156,13 @@ namespace Ryujinx.Core.OsHle.Kernel
private void SvcSetThreadActivity(AThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
bool Active = (int)ThreadState.X1 != 0;
bool Active = (int)ThreadState.X1 == 0;
KThread CurrThread = Process.HandleTable.GetData<KThread>(Handle);
KThread Thread = Process.HandleTable.GetData<KThread>(Handle);
if (CurrThread != null)
if (Thread != null)
{
Process.Scheduler.SetThreadActivity(CurrThread, Active);
Process.Scheduler.SetThreadActivity(Thread, Active);
ThreadState.X0 = 0;
}

View file

@ -1,6 +1,7 @@
using ChocolArm64;
using ChocolArm64.Events;
using ChocolArm64.Memory;
using ChocolArm64.State;
using Ryujinx.Core.Loaders;
using Ryujinx.Core.Loaders.Executables;
using Ryujinx.Core.OsHle.Exceptions;
@ -10,6 +11,7 @@ using Ryujinx.Core.OsHle.Services.Nv;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
namespace Ryujinx.Core.OsHle
{
@ -47,9 +49,11 @@ namespace Ryujinx.Core.OsHle
private ConcurrentDictionary<long, KThread> Threads;
private KThread MainThread;
private List<Executable> Executables;
private KThread MainThread;
private Dictionary<long, string> SymbolTable;
private long ImageBase;
@ -121,6 +125,8 @@ namespace Ryujinx.Core.OsHle
return false;
}
MakeSymbolTable();
MapRWMemRegion(
MemoryRegions.MainStackAddress,
MemoryRegions.MainStackSize,
@ -227,20 +233,23 @@ namespace Ryujinx.Core.OsHle
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
}
private void MakeSymbolTable()
{
SymbolTable = new Dictionary<long, string>();
foreach (Executable Exe in Executables)
{
foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
{
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
}
}
}
private ATranslator GetTranslator()
{
if (Translator == null)
{
Dictionary<long, string> SymbolTable = new Dictionary<long, string>();
foreach (Executable Exe in Executables)
{
foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
{
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
}
}
Translator = new ATranslator(SymbolTable);
Translator.CpuTrace += CpuTraceHandler;
@ -249,6 +258,16 @@ namespace Ryujinx.Core.OsHle
return Translator;
}
public void EnableCpuTracing()
{
Translator.EnableCpuTrace = true;
}
public void DisableCpuTracing()
{
Translator.EnableCpuTrace = false;
}
private void CpuTraceHandler(object sender, ACpuTraceEventArgs e)
{
string NsoName = string.Empty;
@ -263,17 +282,47 @@ namespace Ryujinx.Core.OsHle
}
}
Logging.Trace(LogClass.Loader, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
Logging.Trace(LogClass.CPU, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
}
public void EnableCpuTracing()
public void PrintStackTrace(AThreadState ThreadState)
{
Translator.EnableCpuTrace = true;
long[] Positions = ThreadState.GetCallStack();
StringBuilder Trace = new StringBuilder();
Trace.AppendLine("Guest stack trace:");
foreach (long Position in Positions)
{
if (!SymbolTable.TryGetValue(Position, out string SubName))
{
SubName = $"Sub{Position:x16}";
}
Trace.AppendLine(" " + SubName + " (" + GetNsoNameAndAddress(Position) + ")");
}
Logging.Trace(LogClass.CPU, Trace.ToString());
}
public void DisableCpuTracing()
private string GetNsoNameAndAddress(long Position)
{
Translator.EnableCpuTrace = false;
string Name = string.Empty;
for (int Index = Executables.Count - 1; Index >= 0; Index--)
{
if (Position >= Executables[Index].ImageBase)
{
long Offset = Position - Executables[Index].ImageBase;
Name = $"{Executables[Index].Name}:{Offset:x8}";
break;
}
}
return Name;
}
private int GetFreeTlsSlot(AThread Thread)

View file

@ -18,6 +18,7 @@ namespace Ryujinx.Core.OsHle.Services.Am
{ 20, EnsureSaveData },
{ 21, GetDesiredLanguage },
{ 22, SetTerminateResult },
{ 23, GetDisplayVersion },
{ 40, NotifyRunning }
};
}
@ -67,6 +68,15 @@ namespace Ryujinx.Core.OsHle.Services.Am
return 0;
}
public long GetDisplayVersion(ServiceCtx Context)
{
//FIXME: Need to check correct version on a switch.
Context.ResponseData.Write(1L);
Context.ResponseData.Write(0L);
return 0;
}
public long NotifyRunning(ServiceCtx Context)
{
Context.ResponseData.Write(1);

View file

@ -6,7 +6,7 @@ using System.Text;
namespace Ryujinx.Core.OsHle.Services.Aud
{
class IAudioDeviceService : IpcService
class IAudioDevice : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
@ -14,12 +14,13 @@ namespace Ryujinx.Core.OsHle.Services.Aud
private KEvent SystemEvent;
public IAudioDeviceService()
public IAudioDevice()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, ListAudioDeviceName },
{ 1, SetAudioDeviceOutputVolume },
{ 3, GetActiveAudioDeviceName },
{ 4, QueryAudioDeviceSystemEvent },
{ 5, GetActiveChannelCount }
};
@ -72,6 +73,20 @@ namespace Ryujinx.Core.OsHle.Services.Aud
return 0;
}
public long GetActiveAudioDeviceName(ServiceCtx Context)
{
string Name = "FIXME";
long Position = Context.Request.ReceiveBuff[0].Position;
long Size = Context.Request.ReceiveBuff[0].Size;
byte[] Buffer = Encoding.ASCII.GetBytes(Name + '\0');
AMemoryHelper.WriteBytes(Context.Memory, Position, Buffer);
return 0;
}
public long QueryAudioDeviceSystemEvent(ServiceCtx Context)
{
int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent);

View file

@ -53,7 +53,7 @@ namespace Ryujinx.Core.OsHle.Services.Aud
{
long UserId = Context.RequestData.ReadInt64();
MakeObject(Context, new IAudioDeviceService());
MakeObject(Context, new IAudioDevice());
return 0;
}

View file

@ -0,0 +1,20 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Caps
{
class IAlbumAccessorService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IAlbumAccessorService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//...
};
}
}
}

View file

@ -0,0 +1,20 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Caps
{
class IScreenshotService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IScreenshotService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//...
};
}
}
}

View file

@ -3,6 +3,7 @@ using Ryujinx.Core.OsHle.Services.Am;
using Ryujinx.Core.OsHle.Services.Apm;
using Ryujinx.Core.OsHle.Services.Aud;
using Ryujinx.Core.OsHle.Services.Bsd;
using Ryujinx.Core.OsHle.Services.Caps;
using Ryujinx.Core.OsHle.Services.Friend;
using Ryujinx.Core.OsHle.Services.FspSrv;
using Ryujinx.Core.OsHle.Services.Hid;
@ -57,9 +58,18 @@ namespace Ryujinx.Core.OsHle.Services
case "bsd:u":
return new IClient();
case "caps:a":
return new IAlbumAccessorService();
case "caps:ss":
return new IScreenshotService();
case "friend:a":
return new IServiceCreator();
case "friend:u":
return new IServiceCreator();
case "fsp-srv":
return new IFileSystemProxy();

View file

@ -16,10 +16,23 @@ namespace Ryujinx.Core.OsHle.Services.Time
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 101, ToCalendarTimeWithMyRule }
{ 0, GetDeviceLocationName },
{ 101, ToCalendarTimeWithMyRule }
};
}
public long GetDeviceLocationName(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceTime, "Stubbed");
for (int Index = 0; Index < 0x24; Index++)
{
Context.ResponseData.Write((byte)0);
}
return 0;
}
public long ToCalendarTimeWithMyRule(ServiceCtx Context)
{
long PosixTime = Context.RequestData.ReadInt64();