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 class AOptimizations
{ {
public static bool DisableMemoryChecks = false; public static bool DisableMemoryChecks = false;
public static bool GenerateCallStack = true;
} }

View file

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

View file

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

View file

@ -1,5 +1,6 @@
using ChocolArm64.Events; using ChocolArm64.Events;
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
namespace ChocolArm64.State namespace ChocolArm64.State
@ -56,10 +57,17 @@ namespace ChocolArm64.State
public event EventHandler<AInstExceptionEventArgs> SvcCall; public event EventHandler<AInstExceptionEventArgs> SvcCall;
public event EventHandler<AInstUndefinedEventArgs> Undefined; public event EventHandler<AInstUndefinedEventArgs> Undefined;
private Stack<long> CallStack;
private static Stopwatch TickCounter; private static Stopwatch TickCounter;
private static double HostTickFreq; private static double HostTickFreq;
public AThreadState()
{
CallStack = new Stack<long>();
}
static AThreadState() static AThreadState()
{ {
HostTickFreq = 1.0 / Stopwatch.Frequency; HostTickFreq = 1.0 / Stopwatch.Frequency;
@ -83,5 +91,27 @@ namespace ChocolArm64.State
{ {
Undefined?.Invoke(this, new AInstUndefinedEventArgs(Position, RawOpCode)); 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)); 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) public void EmitCall(MethodInfo MthdInfo)
{ {
if (MthdInfo == null) if (MthdInfo == null)

View file

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

View file

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

View file

@ -4,6 +4,8 @@ namespace Ryujinx.Core.Loaders.Executables
{ {
class Nro : IExecutable class Nro : IExecutable
{ {
public string Name { get; private set; }
public byte[] Text { get; private set; } public byte[] Text { get; private set; }
public byte[] RO { get; private set; } public byte[] RO { get; private set; }
public byte[] Data { 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 DataOffset { get; private set; }
public int BssSize { 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); BinaryReader Reader = new BinaryReader(Input);
Input.Seek(4, SeekOrigin.Begin); Input.Seek(4, SeekOrigin.Begin);

View file

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

View file

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

View file

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

View file

@ -156,13 +156,13 @@ namespace Ryujinx.Core.OsHle.Kernel
private void SvcSetThreadActivity(AThreadState ThreadState) private void SvcSetThreadActivity(AThreadState ThreadState)
{ {
int Handle = (int)ThreadState.X0; 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; ThreadState.X0 = 0;
} }

View file

@ -1,6 +1,7 @@
using ChocolArm64; using ChocolArm64;
using ChocolArm64.Events; using ChocolArm64.Events;
using ChocolArm64.Memory; using ChocolArm64.Memory;
using ChocolArm64.State;
using Ryujinx.Core.Loaders; using Ryujinx.Core.Loaders;
using Ryujinx.Core.Loaders.Executables; using Ryujinx.Core.Loaders.Executables;
using Ryujinx.Core.OsHle.Exceptions; using Ryujinx.Core.OsHle.Exceptions;
@ -10,6 +11,7 @@ using Ryujinx.Core.OsHle.Services.Nv;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
namespace Ryujinx.Core.OsHle namespace Ryujinx.Core.OsHle
{ {
@ -47,9 +49,11 @@ namespace Ryujinx.Core.OsHle
private ConcurrentDictionary<long, KThread> Threads; private ConcurrentDictionary<long, KThread> Threads;
private KThread MainThread;
private List<Executable> Executables; private List<Executable> Executables;
private KThread MainThread; private Dictionary<long, string> SymbolTable;
private long ImageBase; private long ImageBase;
@ -121,6 +125,8 @@ namespace Ryujinx.Core.OsHle
return false; return false;
} }
MakeSymbolTable();
MapRWMemRegion( MapRWMemRegion(
MemoryRegions.MainStackAddress, MemoryRegions.MainStackAddress,
MemoryRegions.MainStackSize, MemoryRegions.MainStackSize,
@ -227,11 +233,9 @@ namespace Ryujinx.Core.OsHle
throw new UndefinedInstructionException(e.Position, e.RawOpCode); throw new UndefinedInstructionException(e.Position, e.RawOpCode);
} }
private ATranslator GetTranslator() private void MakeSymbolTable()
{ {
if (Translator == null) SymbolTable = new Dictionary<long, string>();
{
Dictionary<long, string> SymbolTable = new Dictionary<long, string>();
foreach (Executable Exe in Executables) foreach (Executable Exe in Executables)
{ {
@ -240,7 +244,12 @@ namespace Ryujinx.Core.OsHle
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value); SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
} }
} }
}
private ATranslator GetTranslator()
{
if (Translator == null)
{
Translator = new ATranslator(SymbolTable); Translator = new ATranslator(SymbolTable);
Translator.CpuTrace += CpuTraceHandler; Translator.CpuTrace += CpuTraceHandler;
@ -249,6 +258,16 @@ namespace Ryujinx.Core.OsHle
return Translator; return Translator;
} }
public void EnableCpuTracing()
{
Translator.EnableCpuTrace = true;
}
public void DisableCpuTracing()
{
Translator.EnableCpuTrace = false;
}
private void CpuTraceHandler(object sender, ACpuTraceEventArgs e) private void CpuTraceHandler(object sender, ACpuTraceEventArgs e)
{ {
string NsoName = string.Empty; 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}";
} }
public void DisableCpuTracing() Trace.AppendLine(" " + SubName + " (" + GetNsoNameAndAddress(Position) + ")");
}
Logging.Trace(LogClass.CPU, Trace.ToString());
}
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) private int GetFreeTlsSlot(AThread Thread)

View file

@ -18,6 +18,7 @@ namespace Ryujinx.Core.OsHle.Services.Am
{ 20, EnsureSaveData }, { 20, EnsureSaveData },
{ 21, GetDesiredLanguage }, { 21, GetDesiredLanguage },
{ 22, SetTerminateResult }, { 22, SetTerminateResult },
{ 23, GetDisplayVersion },
{ 40, NotifyRunning } { 40, NotifyRunning }
}; };
} }
@ -67,6 +68,15 @@ namespace Ryujinx.Core.OsHle.Services.Am
return 0; 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) public long NotifyRunning(ServiceCtx Context)
{ {
Context.ResponseData.Write(1); Context.ResponseData.Write(1);

View file

@ -6,7 +6,7 @@ using System.Text;
namespace Ryujinx.Core.OsHle.Services.Aud namespace Ryujinx.Core.OsHle.Services.Aud
{ {
class IAudioDeviceService : IpcService class IAudioDevice : IpcService
{ {
private Dictionary<int, ServiceProcessRequest> m_Commands; private Dictionary<int, ServiceProcessRequest> m_Commands;
@ -14,12 +14,13 @@ namespace Ryujinx.Core.OsHle.Services.Aud
private KEvent SystemEvent; private KEvent SystemEvent;
public IAudioDeviceService() public IAudioDevice()
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
{ {
{ 0, ListAudioDeviceName }, { 0, ListAudioDeviceName },
{ 1, SetAudioDeviceOutputVolume }, { 1, SetAudioDeviceOutputVolume },
{ 3, GetActiveAudioDeviceName },
{ 4, QueryAudioDeviceSystemEvent }, { 4, QueryAudioDeviceSystemEvent },
{ 5, GetActiveChannelCount } { 5, GetActiveChannelCount }
}; };
@ -72,6 +73,20 @@ namespace Ryujinx.Core.OsHle.Services.Aud
return 0; 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) public long QueryAudioDeviceSystemEvent(ServiceCtx Context)
{ {
int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent); int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent);

View file

@ -53,7 +53,7 @@ namespace Ryujinx.Core.OsHle.Services.Aud
{ {
long UserId = Context.RequestData.ReadInt64(); long UserId = Context.RequestData.ReadInt64();
MakeObject(Context, new IAudioDeviceService()); MakeObject(Context, new IAudioDevice());
return 0; 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.Apm;
using Ryujinx.Core.OsHle.Services.Aud; using Ryujinx.Core.OsHle.Services.Aud;
using Ryujinx.Core.OsHle.Services.Bsd; using Ryujinx.Core.OsHle.Services.Bsd;
using Ryujinx.Core.OsHle.Services.Caps;
using Ryujinx.Core.OsHle.Services.Friend; using Ryujinx.Core.OsHle.Services.Friend;
using Ryujinx.Core.OsHle.Services.FspSrv; using Ryujinx.Core.OsHle.Services.FspSrv;
using Ryujinx.Core.OsHle.Services.Hid; using Ryujinx.Core.OsHle.Services.Hid;
@ -57,9 +58,18 @@ namespace Ryujinx.Core.OsHle.Services
case "bsd:u": case "bsd:u":
return new IClient(); return new IClient();
case "caps:a":
return new IAlbumAccessorService();
case "caps:ss":
return new IScreenshotService();
case "friend:a": case "friend:a":
return new IServiceCreator(); return new IServiceCreator();
case "friend:u":
return new IServiceCreator();
case "fsp-srv": case "fsp-srv":
return new IFileSystemProxy(); return new IFileSystemProxy();

View file

@ -16,10 +16,23 @@ namespace Ryujinx.Core.OsHle.Services.Time
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
{ {
{ 0, GetDeviceLocationName },
{ 101, ToCalendarTimeWithMyRule } { 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) public long ToCalendarTimeWithMyRule(ServiceCtx Context)
{ {
long PosixTime = Context.RequestData.ReadInt64(); long PosixTime = Context.RequestData.ReadInt64();