aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2018-09-19 17:07:56 -0300
committerGitHub <noreply@github.com>2018-09-19 17:07:56 -0300
commit6d65e536642a7cff7afa34be10fdc8ca66a6e79c (patch)
tree6e7344ea3f5ea7bb84c3b11c241ff7f579457c3e
parent99b2692425ff4045f103cde0745624b9b41d6fe6 (diff)
Remove cold methods from the CPU cache (#224)
* Remove unused tracing functionality from the CPU * GetNsoExecutable -> GetExecutable * Unsigned comparison * Re-add cpu tracing * Config change * Remove cold methods from the translation cache on the cpu * Replace lock with try lock, pass new ATranslatorCache instead of ATranslator * Rebase fixups
-rw-r--r--ChocolArm64/AOptimizations.cs2
-rw-r--r--ChocolArm64/ATranslatedSub.cs8
-rw-r--r--ChocolArm64/ATranslator.cs89
-rw-r--r--ChocolArm64/ATranslatorCache.cs164
-rw-r--r--ChocolArm64/Decoder/ADecoder.cs16
-rw-r--r--ChocolArm64/Events/ACpuTraceEventArgs.cs5
-rw-r--r--ChocolArm64/Instruction/AInstEmitFlow.cs31
-rw-r--r--ChocolArm64/State/AThreadState.cs30
-rw-r--r--ChocolArm64/Translation/AILEmitterCtx.cs44
-rw-r--r--Ryujinx.HLE/HOS/Process.cs138
-rw-r--r--Ryujinx.HLE/Loaders/Executable.cs19
11 files changed, 316 insertions, 230 deletions
diff --git a/ChocolArm64/AOptimizations.cs b/ChocolArm64/AOptimizations.cs
index fbf26a49..40e1674a 100644
--- a/ChocolArm64/AOptimizations.cs
+++ b/ChocolArm64/AOptimizations.cs
@@ -2,8 +2,6 @@ using System.Runtime.Intrinsics.X86;
public static class AOptimizations
{
- public static bool GenerateCallStack = true;
-
private static bool UseAllSseIfAvailable = true;
private static bool UseSseIfAvailable = true;
diff --git a/ChocolArm64/ATranslatedSub.cs b/ChocolArm64/ATranslatedSub.cs
index 9dbc378e..a11da264 100644
--- a/ChocolArm64/ATranslatedSub.cs
+++ b/ChocolArm64/ATranslatedSub.cs
@@ -13,6 +13,8 @@ namespace ChocolArm64
{
private delegate long AA64Subroutine(AThreadState Register, AMemory Memory);
+ private const int MinCallCountForReJit = 250;
+
private AA64Subroutine ExecDelegate;
public static int StateArgIdx { get; private set; }
@@ -32,8 +34,6 @@ namespace ChocolArm64
private bool NeedsReJit;
- private int MinCallCountForReJit = 250;
-
public ATranslatedSub(DynamicMethod Method, List<ARegister> Params)
{
if (Method == null)
@@ -46,8 +46,8 @@ namespace ChocolArm64
throw new ArgumentNullException(nameof(Params));
}
- this.Method = Method;
- this.Params = Params.AsReadOnly();
+ this.Method = Method;
+ this.Params = Params.AsReadOnly();
Callers = new HashSet<long>();
diff --git a/ChocolArm64/ATranslator.cs b/ChocolArm64/ATranslator.cs
index 2d9fcb14..5be41d3e 100644
--- a/ChocolArm64/ATranslator.cs
+++ b/ChocolArm64/ATranslator.cs
@@ -1,38 +1,24 @@
using ChocolArm64.Decoder;
using ChocolArm64.Events;
-using ChocolArm64.Instruction;
using ChocolArm64.Memory;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
using System.Reflection.Emit;
namespace ChocolArm64
{
public class ATranslator
{
- private ConcurrentDictionary<long, ATranslatedSub> CachedSubs;
-
- private ConcurrentDictionary<long, string> SymbolTable;
+ private ATranslatorCache Cache;
public event EventHandler<ACpuTraceEventArgs> CpuTrace;
public bool EnableCpuTrace { get; set; }
- public ATranslator(IReadOnlyDictionary<long, string> SymbolTable = null)
+ public ATranslator()
{
- CachedSubs = new ConcurrentDictionary<long, ATranslatedSub>();
-
- if (SymbolTable != null)
- {
- this.SymbolTable = new ConcurrentDictionary<long, string>(SymbolTable);
- }
- else
- {
- this.SymbolTable = new ConcurrentDictionary<long, string>();
- }
+ Cache = new ATranslatorCache();
}
internal void ExecuteSubroutine(AThread Thread, long Position)
@@ -70,15 +56,10 @@ namespace ChocolArm64
{
if (EnableCpuTrace)
{
- if (!SymbolTable.TryGetValue(Position, out string SubName))
- {
- SubName = string.Empty;
- }
-
- CpuTrace?.Invoke(this, new ACpuTraceEventArgs(Position, SubName));
+ CpuTrace?.Invoke(this, new ACpuTraceEventArgs(Position));
}
- if (!CachedSubs.TryGetValue(Position, out ATranslatedSub Sub))
+ if (!Cache.TryGetSubroutine(Position, out ATranslatedSub Sub))
{
Sub = TranslateTier0(State, Memory, Position);
}
@@ -93,37 +74,20 @@ namespace ChocolArm64
while (Position != 0 && State.Running);
}
- internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub)
- {
- if (OpCode.Emitter != AInstEmit.Bl)
- {
- Sub = null;
-
- return false;
- }
-
- return TryGetCachedSub(((AOpCodeBImmAl)OpCode).Imm, out Sub);
- }
-
- internal bool TryGetCachedSub(long Position, out ATranslatedSub Sub)
- {
- return CachedSubs.TryGetValue(Position, out Sub);
- }
-
internal bool HasCachedSub(long Position)
{
- return CachedSubs.ContainsKey(Position);
+ return Cache.HasSubroutine(Position);
}
private ATranslatedSub TranslateTier0(AThreadState State, AMemory Memory, long Position)
{
- ABlock Block = ADecoder.DecodeBasicBlock(State, this, Memory, Position);
+ ABlock Block = ADecoder.DecodeBasicBlock(State, Memory, Position);
ABlock[] Graph = new ABlock[] { Block };
- string SubName = GetSubName(Position);
+ string SubName = GetSubroutineName(Position);
- AILEmitterCtx Context = new AILEmitterCtx(this, Graph, Block, SubName);
+ AILEmitterCtx Context = new AILEmitterCtx(Cache, Graph, Block, SubName);
do
{
@@ -135,7 +99,7 @@ namespace ChocolArm64
Subroutine.SetType(ATranslatedSubType.SubTier0);
- CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine);
+ Cache.AddOrUpdate(Position, Subroutine, Block.OpCodes.Count);
AOpCode LastOp = Block.GetLastOp();
@@ -144,13 +108,11 @@ namespace ChocolArm64
private void TranslateTier1(AThreadState State, AMemory Memory, long Position)
{
- (ABlock[] Graph, ABlock Root) Cfg = ADecoder.DecodeSubroutine(State, this, Memory, Position);
+ (ABlock[] Graph, ABlock Root) = ADecoder.DecodeSubroutine(Cache, State, Memory, Position);
- string SubName = GetSubName(Position);
+ string SubName = GetSubroutineName(Position);
- PropagateName(Cfg.Graph, SubName);
-
- AILEmitterCtx Context = new AILEmitterCtx(this, Cfg.Graph, Cfg.Root, SubName);
+ AILEmitterCtx Context = new AILEmitterCtx(Cache, Graph, Root, SubName);
if (Context.CurrBlock.Position != Position)
{
@@ -165,11 +127,11 @@ namespace ChocolArm64
//Mark all methods that calls this method for ReJiting,
//since we can now call it directly which is faster.
- if (CachedSubs.TryGetValue(Position, out ATranslatedSub OldSub))
+ if (Cache.TryGetSubroutine(Position, out ATranslatedSub OldSub))
{
foreach (long CallerPos in OldSub.GetCallerPositions())
{
- if (CachedSubs.TryGetValue(Position, out ATranslatedSub CallerSub))
+ if (Cache.TryGetSubroutine(Position, out ATranslatedSub CallerSub))
{
CallerSub.MarkForReJit();
}
@@ -180,27 +142,24 @@ namespace ChocolArm64
Subroutine.SetType(ATranslatedSubType.SubTier1);
- CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine);
+ Cache.AddOrUpdate(Position, Subroutine, GetGraphInstCount(Graph));
}
- private string GetSubName(long Position)
+ private string GetSubroutineName(long Position)
{
- return SymbolTable.GetOrAdd(Position, $"Sub{Position:x16}");
+ return $"Sub{Position:x16}";
}
- private void PropagateName(ABlock[] Graph, string Name)
+ private int GetGraphInstCount(ABlock[] Graph)
{
+ int Size = 0;
+
foreach (ABlock Block in Graph)
{
- AOpCode LastOp = Block.GetLastOp();
-
- if (LastOp != null &&
- (LastOp.Emitter == AInstEmit.Bl ||
- LastOp.Emitter == AInstEmit.Blr))
- {
- SymbolTable.TryAdd(LastOp.Position + 4, Name);
- }
+ Size += Block.OpCodes.Count;
}
+
+ return Size;
}
}
} \ No newline at end of file
diff --git a/ChocolArm64/ATranslatorCache.cs b/ChocolArm64/ATranslatorCache.cs
new file mode 100644
index 00000000..e34cc397
--- /dev/null
+++ b/ChocolArm64/ATranslatorCache.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+namespace ChocolArm64
+{
+ class ATranslatorCache
+ {
+ private const int MaxTotalSize = 2 * 1024 * 256;
+ private const int MaxTimeDelta = 30000;
+ private const int MinCallCountForUpdate = 1000;
+
+ private class CacheBucket
+ {
+ public ATranslatedSub Subroutine { get; private set; }
+
+ public LinkedListNode<long> Node { get; private set; }
+
+ public int CallCount { get; set; }
+
+ public int Size { get; private set; }
+
+ public int Timestamp { get; private set; }
+
+ public CacheBucket(ATranslatedSub Subroutine, LinkedListNode<long> Node, int Size)
+ {
+ this.Subroutine = Subroutine;
+ this.Size = Size;
+
+ UpdateNode(Node);
+ }
+
+ public void UpdateNode(LinkedListNode<long> Node)
+ {
+ this.Node = Node;
+
+ Timestamp = Environment.TickCount;
+ }
+ }
+
+ private ConcurrentDictionary<long, CacheBucket> Cache;
+
+ private LinkedList<long> SortedCache;
+
+ private int TotalSize;
+
+ public ATranslatorCache()
+ {
+ Cache = new ConcurrentDictionary<long, CacheBucket>();
+
+ SortedCache = new LinkedList<long>();
+ }
+
+ public void AddOrUpdate(long Position, ATranslatedSub Subroutine, int Size)
+ {
+ ClearCacheIfNeeded();
+
+ TotalSize += Size;
+
+ lock (SortedCache)
+ {
+ LinkedListNode<long> Node = SortedCache.AddLast(Position);
+
+ CacheBucket NewBucket = new CacheBucket(Subroutine, Node, Size);
+
+ Cache.AddOrUpdate(Position, NewBucket, (Key, Bucket) =>
+ {
+ TotalSize -= Bucket.Size;
+
+ SortedCache.Remove(Bucket.Node);
+
+ return NewBucket;
+ });
+ }
+ }
+
+ public bool HasSubroutine(long Position)
+ {
+ return Cache.ContainsKey(Position);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetSubroutine(long Position, out ATranslatedSub Subroutine)
+ {
+ if (Cache.TryGetValue(Position, out CacheBucket Bucket))
+ {
+ if (Bucket.CallCount++ > MinCallCountForUpdate)
+ {
+ if (Monitor.TryEnter(SortedCache))
+ {
+ try
+ {
+ Bucket.CallCount = 0;
+
+ SortedCache.Remove(Bucket.Node);
+
+ Bucket.UpdateNode(SortedCache.AddLast(Position));
+ }
+ finally
+ {
+ Monitor.Exit(SortedCache);
+ }
+ }
+ }
+
+ Subroutine = Bucket.Subroutine;
+
+ return true;
+ }
+
+ Subroutine = default(ATranslatedSub);
+
+ return false;
+ }
+
+ private void ClearCacheIfNeeded()
+ {
+ int Timestamp = Environment.TickCount;
+
+ while (TotalSize > MaxTotalSize)
+ {
+ lock (SortedCache)
+ {
+ LinkedListNode<long> Node = SortedCache.First;
+
+ if (Node == null)
+ {
+ break;
+ }
+
+ CacheBucket Bucket = Cache[Node.Value];
+
+ int TimeDelta = RingDelta(Bucket.Timestamp, Timestamp);
+
+ if ((uint)TimeDelta <= (uint)MaxTimeDelta)
+ {
+ break;
+ }
+
+ if (Cache.TryRemove(Node.Value, out Bucket))
+ {
+ TotalSize -= Bucket.Size;
+
+ SortedCache.Remove(Bucket.Node);
+ }
+ }
+ }
+ }
+
+ private static int RingDelta(int Old, int New)
+ {
+ if ((uint)New < (uint)Old)
+ {
+ return New + (~Old + 1);
+ }
+ else
+ {
+ return New - Old;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Decoder/ADecoder.cs b/ChocolArm64/Decoder/ADecoder.cs
index b154a54c..64beebd2 100644
--- a/ChocolArm64/Decoder/ADecoder.cs
+++ b/ChocolArm64/Decoder/ADecoder.cs
@@ -19,11 +19,7 @@ namespace ChocolArm64.Decoder
OpActivators = new ConcurrentDictionary<Type, OpActivator>();
}
- public static ABlock DecodeBasicBlock(
- AThreadState State,
- ATranslator Translator,
- AMemory Memory,
- long Start)
+ public static ABlock DecodeBasicBlock(AThreadState State, AMemory Memory, long Start)
{
ABlock Block = new ABlock(Start);
@@ -33,10 +29,10 @@ namespace ChocolArm64.Decoder
}
public static (ABlock[] Graph, ABlock Root) DecodeSubroutine(
- AThreadState State,
- ATranslator Translator,
- AMemory Memory,
- long Start)
+ ATranslatorCache Cache,
+ AThreadState State,
+ AMemory Memory,
+ long Start)
{
Dictionary<long, ABlock> Visited = new Dictionary<long, ABlock>();
Dictionary<long, ABlock> VisitedEnd = new Dictionary<long, ABlock>();
@@ -79,7 +75,7 @@ namespace ChocolArm64.Decoder
{
if (Op.Emitter == AInstEmit.Bl)
{
- HasCachedSub = Translator.HasCachedSub(Op.Imm);
+ HasCachedSub = Cache.HasSubroutine(Op.Imm);
}
else
{
diff --git a/ChocolArm64/Events/ACpuTraceEventArgs.cs b/ChocolArm64/Events/ACpuTraceEventArgs.cs
index fedf3865..0284f4ee 100644
--- a/ChocolArm64/Events/ACpuTraceEventArgs.cs
+++ b/ChocolArm64/Events/ACpuTraceEventArgs.cs
@@ -6,12 +6,9 @@ namespace ChocolArm64.Events
{
public long Position { get; private set; }
- public string SubName { get; private set; }
-
- public ACpuTraceEventArgs(long Position, string SubName)
+ public ACpuTraceEventArgs(long Position)
{
this.Position = Position;
- this.SubName = SubName;
}
}
} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitFlow.cs b/ChocolArm64/Instruction/AInstEmitFlow.cs
index 89979d05..91262834 100644
--- a/ChocolArm64/Instruction/AInstEmitFlow.cs
+++ b/ChocolArm64/Instruction/AInstEmitFlow.cs
@@ -35,14 +35,6 @@ 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();
@@ -80,14 +72,6 @@ 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();
@@ -100,14 +84,6 @@ 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);
@@ -129,13 +105,6 @@ 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);
diff --git a/ChocolArm64/State/AThreadState.cs b/ChocolArm64/State/AThreadState.cs
index 22e3df14..e4953b02 100644
--- a/ChocolArm64/State/AThreadState.cs
+++ b/ChocolArm64/State/AThreadState.cs
@@ -1,6 +1,5 @@
using ChocolArm64.Events;
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
@@ -86,17 +85,10 @@ 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;
@@ -153,27 +145,5 @@ 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();
- }
}
} \ No newline at end of file
diff --git a/ChocolArm64/Translation/AILEmitterCtx.cs b/ChocolArm64/Translation/AILEmitterCtx.cs
index 48700715..cad0d323 100644
--- a/ChocolArm64/Translation/AILEmitterCtx.cs
+++ b/ChocolArm64/Translation/AILEmitterCtx.cs
@@ -10,7 +10,7 @@ namespace ChocolArm64.Translation
{
class AILEmitterCtx
{
- private ATranslator Translator;
+ private ATranslatorCache Cache;
private Dictionary<long, AILLabel> Labels;
@@ -40,29 +40,14 @@ namespace ChocolArm64.Translation
private const int Tmp5Index = -5;
public AILEmitterCtx(
- ATranslator Translator,
- ABlock[] Graph,
- ABlock Root,
- string SubName)
+ ATranslatorCache Cache,
+ ABlock[] Graph,
+ ABlock Root,
+ string SubName)
{
- if (Translator == null)
- {
- throw new ArgumentNullException(nameof(Translator));
- }
-
- if (Graph == null)
- {
- throw new ArgumentNullException(nameof(Graph));
- }
-
- if (Root == null)
- {
- throw new ArgumentNullException(nameof(Root));
- }
-
- this.Translator = Translator;
- this.Graph = Graph;
- this.Root = Root;
+ this.Cache = Cache ?? throw new ArgumentNullException(nameof(Cache));
+ this.Graph = Graph ?? throw new ArgumentNullException(nameof(Graph));
+ this.Root = Root ?? throw new ArgumentNullException(nameof(Root));
Labels = new Dictionary<long, AILLabel>();
@@ -147,7 +132,12 @@ namespace ChocolArm64.Translation
return false;
}
- if (!Translator.TryGetCachedSub(CurrOp, out ATranslatedSub Sub))
+ if (CurrOp.Emitter != AInstEmit.Bl)
+ {
+ return false;
+ }
+
+ if (!Cache.TryGetSubroutine(((AOpCodeBImmAl)CurrOp).Imm, out ATranslatedSub Subroutine))
{
return false;
}
@@ -157,7 +147,7 @@ namespace ChocolArm64.Translation
EmitLdarg(Index);
}
- foreach (ARegister Reg in Sub.Params)
+ foreach (ARegister Reg in Subroutine.Params)
{
switch (Reg.Type)
{
@@ -167,9 +157,9 @@ namespace ChocolArm64.Translation
}
}
- EmitCall(Sub.Method);
+ EmitCall(Subroutine.Method);
- Sub.AddCaller(Root.Position);
+ Subroutine.AddCaller(Root.Position);
return true;
}
diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs
index 0bc95f0c..f7ec2604 100644
--- a/Ryujinx.HLE/HOS/Process.cs
+++ b/Ryujinx.HLE/HOS/Process.cs
@@ -55,8 +55,6 @@ namespace Ryujinx.HLE.HOS
private List<Executable> Executables;
- private Dictionary<long, string> SymbolTable;
-
private long ImageBase;
private bool Disposed;
@@ -122,8 +120,6 @@ namespace Ryujinx.HLE.HOS
return false;
}
- MakeSymbolTable();
-
long MainStackTop = MemoryManager.CodeRegionEnd - KMemoryManager.PageSize;
long MainStackSize = 1 * 1024 * 1024;
@@ -256,69 +252,65 @@ namespace Ryujinx.HLE.HOS
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
}
- private void MakeSymbolTable()
+ public void EnableCpuTracing()
{
- SymbolTable = new Dictionary<long, string>();
+ Translator.EnableCpuTrace = true;
+ }
- foreach (Executable Exe in Executables)
- {
- foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
- {
- SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
- }
- }
+ public void DisableCpuTracing()
+ {
+ Translator.EnableCpuTrace = false;
}
- private ATranslator GetTranslator()
+ private void CpuTraceHandler(object sender, ACpuTraceEventArgs e)
{
- if (Translator == null)
+ Executable Exe = GetExecutable(e.Position);
+
+ if (Exe == null)
{
- Translator = new ATranslator(SymbolTable);
+ return;
+ }
- Translator.CpuTrace += CpuTraceHandler;
+ if (!TryGetSubName(Exe, e.Position, out string SubName))
+ {
+ SubName = string.Empty;
}
- return Translator;
- }
+ long Offset = e.Position - Exe.ImageBase;
- public void EnableCpuTracing()
- {
- Translator.EnableCpuTrace = true;
- }
+ string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}";
- public void DisableCpuTracing()
- {
- Translator.EnableCpuTrace = false;
+ Device.Log.PrintDebug(LogClass.Cpu, ExeNameWithAddr + " " + SubName);
}
- private void CpuTraceHandler(object sender, ACpuTraceEventArgs e)
+ private ATranslator GetTranslator()
{
- string NsoName = string.Empty;
-
- for (int Index = Executables.Count - 1; Index >= 0; Index--)
+ if (Translator == null)
{
- if (e.Position >= Executables[Index].ImageBase)
- {
- NsoName = $"{(e.Position - Executables[Index].ImageBase):x16}";
+ Translator = new ATranslator();
- break;
- }
+ Translator.CpuTrace += CpuTraceHandler;
}
- Device.Log.PrintDebug(LogClass.Cpu, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
+ return Translator;
}
public void PrintStackTrace(AThreadState ThreadState)
{
- long[] Positions = ThreadState.GetCallStack();
-
StringBuilder Trace = new StringBuilder();
Trace.AppendLine("Guest stack trace:");
- foreach (long Position in Positions)
+ void AppendTrace(long Position)
{
- if (!SymbolTable.TryGetValue(Position, out string SubName))
+ Executable Exe = GetExecutable(Position);
+
+ if (Exe == null)
+ {
+ return;
+ }
+
+ if (!TryGetSubName(Exe, Position, out string SubName))
{
SubName = $"Sub{Position:x16}";
}
@@ -327,29 +319,77 @@ namespace Ryujinx.HLE.HOS
SubName = Demangler.Parse(SubName);
}
- Trace.AppendLine(" " + SubName + " (" + GetNsoNameAndAddress(Position) + ")");
+ long Offset = Position - Exe.ImageBase;
+
+ string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}";
+
+ Trace.AppendLine(" " + ExeNameWithAddr + " " + SubName);
+ }
+
+ long FramePointer = (long)ThreadState.X29;
+
+ while (FramePointer != 0)
+ {
+ AppendTrace(Memory.ReadInt64(FramePointer + 8));
+
+ FramePointer = Memory.ReadInt64(FramePointer);
}
Device.Log.PrintInfo(LogClass.Cpu, Trace.ToString());
}
- private string GetNsoNameAndAddress(long Position)
+ private bool TryGetSubName(Executable Exe, long Position, out string Name)
{
- string Name = string.Empty;
+ Position -= Exe.ImageBase;
- for (int Index = Executables.Count - 1; Index >= 0; Index--)
+ int Left = 0;
+ int Right = Exe.SymbolTable.Count - 1;
+
+ while (Left <= Right)
{
- if (Position >= Executables[Index].ImageBase)
+ int Size = Right - Left;
+
+ int Middle = Left + (Size >> 1);
+
+ ElfSym Symbol = Exe.SymbolTable[Middle];
+
+ long EndPosition = Symbol.Value + Symbol.Size;
+
+ if ((ulong)Position >= (ulong)Symbol.Value && (ulong)Position < (ulong)EndPosition)
+ {
+ Name = Symbol.Name;
+
+ return true;
+ }
+
+ if ((ulong)Position < (ulong)Symbol.Value)
{
- long Offset = Position - Executables[Index].ImageBase;
+ Right = Middle - 1;
+ }
+ else
+ {
+ Left = Middle + 1;
+ }
+ }
+
+ Name = null;
- Name = $"{Executables[Index].Name}:{Offset:x8}";
+ return false;
+ }
- break;
+ private Executable GetExecutable(long Position)
+ {
+ string Name = string.Empty;
+
+ for (int Index = Executables.Count - 1; Index >= 0; Index--)
+ {
+ if ((ulong)Position >= (ulong)Executables[Index].ImageBase)
+ {
+ return Executables[Index];
}
}
- return Name;
+ return null;
}
private void ThreadFinished(object sender, EventArgs e)
diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs
index 6a3f0b97..a9850e4a 100644
--- a/Ryujinx.HLE/Loaders/Executable.cs
+++ b/Ryujinx.HLE/Loaders/Executable.cs
@@ -3,18 +3,21 @@ using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Utilities;
+using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.IO;
+using System.Linq;
namespace Ryujinx.HLE.Loaders
{
class Executable
{
- private List<ElfDyn> Dynamic;
+ private AMemory Memory;
- private Dictionary<long, string> m_SymbolTable;
+ private List<ElfDyn> Dynamic;
- public IReadOnlyDictionary<long, string> SymbolTable => m_SymbolTable;
+ public ReadOnlyCollection<ElfSym> SymbolTable;
public string Name { get; private set; }
@@ -23,16 +26,12 @@ namespace Ryujinx.HLE.Loaders
public long ImageBase { get; private set; }
public long ImageEnd { get; private set; }
- private AMemory Memory;
-
private KMemoryManager MemoryManager;
public Executable(IExecutable Exe, KMemoryManager MemoryManager, AMemory Memory, long ImageBase)
{
Dynamic = new List<ElfDyn>();
- m_SymbolTable = new Dictionary<long, string>();
-
FilePath = Exe.FilePath;
if (FilePath != null)
@@ -103,14 +102,18 @@ namespace Ryujinx.HLE.Loaders
long SymEntSize = GetFirstValue(ElfDynTag.DT_SYMENT);
+ List<ElfSym> Symbols = new List<ElfSym>();
+
while ((ulong)SymTblAddr < (ulong)StrTblAddr)
{
ElfSym Sym = GetSymbol(SymTblAddr, StrTblAddr);
- m_SymbolTable.TryAdd(Sym.Value, Sym.Name);
+ Symbols.Add(Sym);
SymTblAddr += SymEntSize;
}
+
+ SymbolTable = Array.AsReadOnly(Symbols.OrderBy(x => x.Value).ToArray());
}
private ElfRel GetRelocation(long Position)