From 0ca4dff62c61570828ec563d6200ca47b3d02569 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 20 Apr 2022 20:17:48 -0400
Subject: [PATCH] core/arm: separate backtrace collection

---
 src/core/arm/arm_interface.cpp            | 97 ++++-------------------
 src/core/arm/arm_interface.h              | 18 +++--
 src/core/arm/dynarmic/arm_dynarmic_32.cpp | 19 +++++
 src/core/arm/dynarmic/arm_dynarmic_32.h   |  8 ++
 src/core/arm/dynarmic/arm_dynarmic_64.cpp | 38 +++++++++
 src/core/arm/dynarmic/arm_dynarmic_64.h   |  8 ++
 6 files changed, 98 insertions(+), 90 deletions(-)

diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 08bf1201d4..2a9390e265 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -14,96 +14,28 @@
 #include "core/loader/loader.h"
 #include "core/memory.h"
 
+#include "core/arm/dynarmic/arm_dynarmic_32.h"
+#include "core/arm/dynarmic/arm_dynarmic_64.h"
+
 namespace Core {
 
 constexpr u64 SEGMENT_BASE = 0x7100000000ull;
 
 std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
-    System& system, const ThreadContext64& ctx) {
-    std::vector<BacktraceEntry> out;
-    auto& memory = system.Memory();
-
-    auto fp = ctx.cpu_registers[29];
-    auto lr = ctx.cpu_registers[30];
-    while (true) {
-        out.push_back({
-            .module = "",
-            .address = 0,
-            .original_address = lr,
-            .offset = 0,
-            .name = {},
-        });
-
-        if (fp == 0) {
-            break;
-        }
-
-        lr = memory.Read64(fp + 8) - 4;
-        fp = memory.Read64(fp);
-    }
-
-    std::map<VAddr, std::string> modules;
-    auto& loader{system.GetAppLoader()};
-    if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
-        return {};
-    }
-
-    std::map<std::string, Symbols::Symbols> symbols;
-    for (const auto& module : modules) {
-        symbols.insert_or_assign(module.second,
-                                 Symbols::GetSymbols(module.first, system.Memory(),
-                                                     system.CurrentProcess()->Is64BitProcess()));
-    }
-
-    for (auto& entry : out) {
-        VAddr base = 0;
-        for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
-            const auto& module{*iter};
-            if (entry.original_address >= module.first) {
-                entry.module = module.second;
-                base = module.first;
-                break;
-            }
-        }
-
-        entry.offset = entry.original_address - base;
-        entry.address = SEGMENT_BASE + entry.offset;
-
-        if (entry.module.empty())
-            entry.module = "unknown";
-
-        const auto symbol_set = symbols.find(entry.module);
-        if (symbol_set != symbols.end()) {
-            const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
-            if (symbol.has_value()) {
-                // TODO(DarkLordZach): Add demangling of symbol names.
-                entry.name = *symbol;
-            }
-        }
-    }
-
-    return out;
+    Core::System& system, const ARM_Interface::ThreadContext32& ctx) {
+    return ARM_Dynarmic_32::GetBacktraceFromContext(system, ctx);
 }
 
-std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
-    std::vector<BacktraceEntry> out;
-    auto& memory = system.Memory();
-
-    auto fp = GetReg(29);
-    auto lr = GetReg(30);
-    while (true) {
-        out.push_back({"", 0, lr, 0, ""});
-        if (!fp) {
-            break;
-        }
-        lr = memory.Read64(fp + 8) - 4;
-        fp = memory.Read64(fp);
-    }
+std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
+    Core::System& system, const ARM_Interface::ThreadContext64& ctx) {
+    return ARM_Dynarmic_64::GetBacktraceFromContext(system, ctx);
+}
 
+void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) {
     std::map<VAddr, std::string> modules;
     auto& loader{system.GetAppLoader()};
     if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
-        return {};
+        return;
     }
 
     std::map<std::string, Symbols::Symbols> symbols;
@@ -127,8 +59,9 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
         entry.offset = entry.original_address - base;
         entry.address = SEGMENT_BASE + entry.offset;
 
-        if (entry.module.empty())
+        if (entry.module.empty()) {
             entry.module = "unknown";
+        }
 
         const auto symbol_set = symbols.find(entry.module);
         if (symbol_set != symbols.end()) {
@@ -139,12 +72,10 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
             }
         }
     }
-
-    return out;
 }
 
 void ARM_Interface::LogBacktrace() const {
-    const VAddr sp = GetReg(13);
+    const VAddr sp = GetSP();
     const VAddr pc = GetPC();
     LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
     LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index dce2f41952..bcec4b3b85 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -100,6 +100,12 @@ public:
      */
     virtual u64 GetPC() const = 0;
 
+    /**
+     * Get the current Stack Pointer
+     * @return Returns current SP
+     */
+    virtual u64 GetSP() const = 0;
+
     /**
      * Get an ARM register
      * @param index Register index
@@ -182,17 +188,13 @@ public:
         std::string name;
     };
 
+    static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+                                                               const ThreadContext32& ctx);
     static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
                                                                const ThreadContext64& ctx);
 
-    std::vector<BacktraceEntry> GetBacktrace() const;
+    virtual std::vector<BacktraceEntry> GetBacktrace() const = 0;
 
-    /// fp (= r29) points to the last frame record.
-    /// Note that this is the frame record for the *previous* frame, not the current one.
-    /// Note we need to subtract 4 from our last read to get the proper address
-    /// Frame records are two words long:
-    /// fp+0 : pointer to previous frame record
-    /// fp+8 : value of lr for frame
     void LogBacktrace() const;
 
 protected:
@@ -200,6 +202,8 @@ protected:
     System& system;
     CPUInterrupts& interrupt_handlers;
     bool uses_wall_clock;
+
+    static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
 };
 
 } // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 5de4384dbc..da5659046a 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -268,6 +268,10 @@ u64 ARM_Dynarmic_32::GetPC() const {
     return jit.load()->Regs()[15];
 }
 
+u64 ARM_Dynarmic_32::GetSP() const {
+    return jit.load()->Regs()[13];
+}
+
 u64 ARM_Dynarmic_32::GetReg(int index) const {
     return jit.load()->Regs()[index];
 }
@@ -362,4 +366,19 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
     jit_cache.emplace(key, std::move(new_jit));
 }
 
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system,
+                                                                         u64 sp, u64 lr) {
+    // No way to get accurate stack traces in A32 yet
+    return {};
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext(
+    System& system, const ThreadContext32& ctx) {
+    return GetBacktrace(system, ctx.cpu_registers[13], ctx.cpu_registers[14]);
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
+    return GetBacktrace(system, GetReg(13), GetReg(14));
+}
+
 } // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 6849373532..1b628f94d4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -35,6 +35,7 @@ public:
 
     void SetPC(u64 pc) override;
     u64 GetPC() const override;
+    u64 GetSP() const override;
     u64 GetReg(int index) const override;
     void SetReg(int index, u64 value) override;
     u128 GetVectorReg(int index) const override;
@@ -66,9 +67,16 @@ public:
     void PageTableChanged(Common::PageTable& new_page_table,
                           std::size_t new_address_space_size_in_bits) override;
 
+    static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+                                                               const ThreadContext32& ctx);
+
+    std::vector<BacktraceEntry> GetBacktrace() const override;
+
 private:
     std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
 
+    static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 sp, u64 lr);
+
     using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
     using JitCacheType =
         std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index ae0b158c26..871d9d10e9 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -328,6 +328,10 @@ u64 ARM_Dynarmic_64::GetPC() const {
     return jit.load()->GetPC();
 }
 
+u64 ARM_Dynarmic_64::GetSP() const {
+    return jit.load()->GetSP();
+}
+
 u64 ARM_Dynarmic_64::GetReg(int index) const {
     return jit.load()->GetRegister(index);
 }
@@ -430,4 +434,38 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
     jit_cache.emplace(key, std::move(new_jit));
 }
 
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
+                                                                         u64 fp, u64 lr) {
+    std::vector<BacktraceEntry> out;
+    auto& memory = system.Memory();
+
+    // fp (= r29) points to the last frame record.
+    // Note that this is the frame record for the *previous* frame, not the current one.
+    // Note we need to subtract 4 from our last read to get the proper address
+    // Frame records are two words long:
+    // fp+0 : pointer to previous frame record
+    // fp+8 : value of lr for frame
+    while (true) {
+        out.push_back({"", 0, lr, 0, ""});
+        if (!fp) {
+            break;
+        }
+        lr = memory.Read64(fp + 8) - 4;
+        fp = memory.Read64(fp);
+    }
+
+    SymbolicateBacktrace(system, out);
+
+    return out;
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext(
+    System& system, const ThreadContext64& ctx) {
+    return GetBacktrace(system, ctx.cpu_registers[29], ctx.cpu_registers[30]);
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
+    return GetBacktrace(system, GetReg(29), GetReg(30));
+}
+
 } // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 86018f1961..78773e293f 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -33,6 +33,7 @@ public:
 
     void SetPC(u64 pc) override;
     u64 GetPC() const override;
+    u64 GetSP() const override;
     u64 GetReg(int index) const override;
     void SetReg(int index, u64 value) override;
     u128 GetVectorReg(int index) const override;
@@ -60,10 +61,17 @@ public:
     void PageTableChanged(Common::PageTable& new_page_table,
                           std::size_t new_address_space_size_in_bits) override;
 
+    static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+                                                               const ThreadContext64& ctx);
+
+    std::vector<BacktraceEntry> GetBacktrace() const override;
+
 private:
     std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
                                                 std::size_t address_space_bits) const;
 
+    static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr);
+
     using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
     using JitCacheType =
         std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;